Built with R version 4.4.0.

1 Introduction

The point of this document is to make open and share-able the research methods used for my dissertation on nearshore fish communities in Alaska working towards a PhD in marine biology at the University of Alaska Fairbanks. Here, I cover most steps of the initial data preparation for my second and third chapters concerning spatial and temporal distributions of nearshore fishes across the state. This and other project files can be accessed via the Kachemak Bay National Estuarine Research Reserve’s github nearshore fish repository. Following this document, next step exploratory data analyses referencing these data can be found at this Rpubs site.

In this document I show the steps taken in wrangling the NOAA Nearshore Fish Atlas (NFA) database. This replaces the separate scripts (named “./FishAtlas_#_description.R”) and combines them into a single, streamlined R markdown file. These R scripts will be archived in case I need to replicate older methods. Namely, I used to use rgdal package for some steps in the wrangling process but that package stopped being supported ca. Oct 2023, moving my workflow to the sf package.

Link to the NFA can be found here, https://alaskafisheries.noaa.gov/mapping/sz/. The entire database can be downloaded as a .csv file, which is what I am using. However, I have additional data files that were either shared with me by NFA content managers or modified from the NOAA NFA data directory for ease of reading into R. I will try to simplify this for others to replicate in the future.

2 Wrangling site data

The idea with this initial step is to define the scale of our samples. Contributors to the NFA did not define their SiteID’s and EventID’s similar to each other, so we’ll need to distinguish between SiteID vs EventID and make sure it’s consistent throughout the data.

I define a SiteID as a unique place in space, i.e., all samples from the same ‘beach’ (‘beach’ will be defined later on). An EventID is a unique pull of the seine. So there can be single or multiple EventID’s associated with a SiteID. Additionally, I introduce a VisitID, which adds a temporal distinction among SiteID’s, i.e., all events from the same beach on the same day.

2.1 Set up

Load required packages, define directory, set options, source scripts:

# Packages
library(tidyverse)
library(lubridate)
library(here)
library(skimr)
library(sf)
library(DT)

# Directory
wd = here()
dirs = wd %>% list.files() %>% str_subset(pattern = "^README|^LICENSE|.md$|.Rproj$", negate = TRUE)
for (i in seq_along(dirs)) {
  name = str_replace_all(dirs[i], "^", "dir.")
  path = str_replace_all(dirs[i], "^", str_c(wd, "/"))
  assign(name, path)
  rm(name, path, i)
}

# Options

# Source

2.2 Read in data

data = read_csv(file.path(dir.data, "Raw Fish Atlas Sites 2024-10-16.csv"),
               show_col_types = FALSE)
GearLookup = read_csv(file.path(dir.data, "FishAtlasExpansion_040422_Lookup_Gear.csv"),
                      show_col_types = FALSE)

2.3 Splitting site and species info

As in typical community data (site x species), we have info tied to site description and info tied to taxa. Notice that EventID is the ‘look-up’ key used in both data sets.

events.1 = data %>%
  select(SiteID, EventID,
         Date,
         Lat, Lon = Long, Region, Location,
         Habitat, TidalStage, Temp_C, Salinity,
         GearSpecific, ProjectName, PointOfContact) %>%
  mutate(Date = mdy(Date)) %>% # re-format date
  distinct()
glimpse(events.1)
## Rows: 4,473
## Columns: 14
## $ SiteID         <dbl> 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 4…
## $ EventID        <dbl> 15, 60, 9, 89, 433, 634, 635, 767, 90, 12, 61, 16, 17, …
## $ Date           <date> 2001-07-24, 2002-03-25, 2001-04-26, 2002-07-24, 2003-0…
## $ Lat            <dbl> 58.57520, 58.57520, 58.57520, 58.57520, 58.57520, 58.57…
## $ Lon            <dbl> -134.9263, -134.9263, -134.9263, -134.9263, -134.9263, …
## $ Region         <chr> "Gulf of Alaska", "Gulf of Alaska", "Gulf of Alaska", "…
## $ Location       <chr> "southeastern Alaska, Lynn Canal, North Island", "south…
## $ Habitat        <chr> "Kelp", "Kelp", "Kelp", "Kelp", "Kelp", "Kelp", "Kelp",…
## $ TidalStage     <chr> "LOW", "LOW", "LOW", "LOW", "LOW", "LOW", "LOW", "LOW",…
## $ Temp_C         <dbl> 13.0, 4.0, 6.0, 13.0, 5.0, 12.0, 12.0, 3.7, 13.0, 6.0, …
## $ Salinity       <dbl> 21, 33, 33, 18, 31, 16, 16, 33, 18, 33, 33, 21, 21, 33,…
## $ GearSpecific   <chr> "BSEINE-STD", "BSEINE-STD", "BSEINE-STD", "BSEINE-STD",…
## $ ProjectName    <chr> "SYNTHESIS", "SYNTHESIS", "SYNTHESIS", "SYNTHESIS", "SY…
## $ PointOfContact <chr> "NMFS, Alaska Fisheries Science Center, Auke Bay Labora…
catch.1 = data %>%
  select(EventID,
         Sp_CommonName, Sp_ScientificName, Fam_CommonName, Fam_ScientificName,
         Unmeasured, Length_mm, LengthType, LifeStage)
glimpse(catch.1)
## Rows: 161,621
## Columns: 9
## $ EventID            <dbl> 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,…
## $ Sp_CommonName      <chr> "Coho salmon", "Great sculpin", "Great sculpin", "P…
## $ Sp_ScientificName  <chr> "Oncorhynchus kisutch", "Myoxocephalus polyacanthoc…
## $ Fam_CommonName     <chr> "trouts and salmons", "sculpins", "sculpins", "cods…
## $ Fam_ScientificName <chr> "Salmonidae", "Cottidae", "Cottidae", "Gadidae", "P…
## $ Unmeasured         <dbl> 0, 0, 0, 0, 0, 10, 3, 1, 4, 0, 0, 0, 0, 0, 0, 0, 0,…
## $ Length_mm          <dbl> 157, 208, 167, 81, 153, NA, NA, NA, NA, 66, 50, 53,…
## $ LengthType         <chr> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA,…
## $ LifeStage          <chr> "JUV", "JUV", "JUV", "YOY", "JUV", "JUV", NA, NA, N…

2.4 Considering gear type

The NFA website makes it easy to filter for gear type, which I did prior to downloading the .csv file. However, we’ll want to account for differences among the beach seines used- namely the net mesh size. This information is contained in the metadata for the NFA database and can be accessed by request from the database managers (file FishAtlasExpansion_040422_Lookup_Gear.csv). A few instances of missing mesh size information was provided by email request to the associated point of contact for that contributed data. Results from the below code are hidden but comments explain what decisions were made.

# Join the mesh size info from our gear data to the site information
gear.1 = left_join(events.1, select(GearLookup, GearSpecific, MeshSize), by = "GearSpecific")

# Subset for gear related info and take a look
gear.2 = select(gear.1, EventID, MeshSize, GearSpecific, ProjectName, PointOfContact)
glimpse(gear.2)
# How many different mesh sizes are there?
gear.2$MeshSize %>% unique()

# Looks like we have an NA to take care of...
filter(gear.2, is.na(MeshSize))
# These are all from the GOAIERP project (PI Olav Ormseth). The report from that project reports 6 mm stretched mesh.
# We'll just go ahead and fix it here instead of changing the GearLookup file,
gear.3 = mutate(gear.2, MeshSize = ifelse(is.na(MeshSize), 6, MeshSize))

# Check our mesh sizes again,
gear.3$MeshSize %>% unique()
# Great, no more NA's.

# We can now join this information with our site data,
events.2 = left_join(events.1, select(gear.3, EventID, MeshSize), by = "EventID")

2.5 The “Site-Event” problem

The catch data is tied to unique EventID’s which are associated with spatially explicit SiteID’s (each SiteID has a unique lat/lon). Considering that the data has been contributed by multiple researchers/projects, we’ll need to make sure we have consistent sites and events across the whole dataset. After that we can tackle the catch data. First, we take a look at all of the site data with skimr::skim().

skim(events.2)
Data summary
Name events.2
Number of rows 4473
Number of columns 15
_______________________
Column type frequency:
character 7
Date 1
numeric 7
________________________
Group variables None

Variable type: character

skim_variable n_missing complete_rate min max empty n_unique whitespace
Region 0 1.00 10 16 0 5 0
Location 1 1.00 8 135 0 406 0
Habitat 0 1.00 4 13 0 8 0
TidalStage 1007 0.77 3 5 0 6 0
GearSpecific 0 1.00 8 12 0 15 0
ProjectName 0 1.00 4 31 0 31 0
PointOfContact 0 1.00 91 151 0 11 0

Variable type: Date

skim_variable n_missing complete_rate min max median n_unique
Date 0 1 1976-05-29 2021-09-09 2006-04-25 1035

Variable type: numeric

skim_variable n_missing complete_rate mean sd p0 p25 p50 p75 p100 hist
SiteID 0 1.00 1173.39 597.51 1.00 725.00 1091.00 1664.00 2505.00 ▅▃▇▇▁
EventID 0 1.00 6345.33 3081.50 1.00 3613.00 6988.00 9176.00 10340.00 ▃▃▁▇▇
Lat 0 1.00 59.66 3.58 51.64 58.33 59.40 59.60 71.39 ▁▇▃▁▁
Lon 0 1.00 -145.21 9.86 -176.95 -151.63 -150.22 -134.95 -130.99 ▁▁▇▁▇
Temp_C 1966 0.56 9.97 3.45 -0.92 7.40 10.50 12.50 28.70 ▂▇▇▁▁
Salinity 1988 0.56 24.07 14.20 -400.00 19.80 25.11 30.10 73.50 ▁▁▁▁▇
MeshSize 0 1.00 5.36 3.52 3.00 3.00 3.20 6.00 12.70 ▇▂▁▁▂

In particular we want to get a sense of how EventIDs are associated with SiteIDs and other variables placing them in space and time.

We see a lot of single-pull samples when events are grouped by SiteID and Date, i.e., a visit. However, we can’t assume all of those ‘visits’ are comparable in scope. Projects differed in structure and purpose, so what one researcher would consider a new site, another researcher might call a replicate event to be combined with other events within a single sample. I call this the “Site-Event” problem.

We see that the distribution of events becomes less severe when grouped by Date and Location, which tells me that some of those single-pull samples actually should be aggregated to be comparable to other multi-pull samples. However, grouping by location may not be accurate because of the loose definition of ‘Location’ (the levels of Location also seem to vary in scale). Since location info is tied to SiteID, we can’t just look at EventID’s and lat/lon. Instead, we’ll need to figure out a way to combine events at the SiteID level. Basically, we are trying to combine SiteID’s that are currently unique but should have the same identifier, i.e., samples of the same beach.

2.6 Solving the “Site-Event” problem

Here is where we need to decide on how to define a ‘beach’. In other words, what do we consider the spatial scale distinguishing one sample from another? From my personal seining, one of my larger-distanced sites was near Anchor Point, AK, where we’ve seined up to 800 m between first and last replicate. So in my opinion, 1 km would be reasonably conservative estimate to link replicate seines. Imagine seining by skiff- you could hop between replicates ~1 km down beach and still consider it the same site. In the below code, I define clusters of SiteID’s by grouping points that are within 1000 m of at least one other SiteID.

# Make a new tibble containing SiteID geometries:
sites.sf = events.2 %>%
  select(SiteID, Lat, Lon) %>%
  # Create sf object from lat/lon:
  st_as_sf(., coords = c("Lon", "Lat"), crs = 4326) %>% # WGS84
  # Project into Alaska Albers 3338
  st_transform(crs = 3338) %>%
  distinct()

# Define a buffer of 1km around each point
sites.sf.buf = st_buffer(sites.sf, dist = 1000)

# Create a tibble with clusters of overlapping buffer areas 
sites.sf.cl = sites.sf.buf %>%
  st_union() %>% # unite buffer areas
  st_cast(to = "POLYGON") %>% # turn them into polygon features
  st_as_sf() %>% # return geometry set features to sfc
  rownames_to_column(var = "Cluster") %>% # create col for cluster groups
  mutate(Cluster = as.factor(Cluster)) # use factor class

# Assign SiteID points to buffer area clusters
sites.sf.cl.join = st_join(sites.sf, sites.sf.cl, left = TRUE)

# Drop sf class from joined data
sites.cl = st_drop_geometry(sites.sf.cl.join)

# See what we got:
glimpse(sites.cl)
## Rows: 1,178
## Columns: 2
## $ SiteID  <dbl> 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18,…
## $ Cluster <fct> 327, 327, 327, 327, 327, 326, 326, 326, 326, 327, 327, 327, 32…

We now have a simple classification variable that can be joined back with the site information.

events.3 = left_join(events.2, sites.cl, by = "SiteID")

However, this only takes into account the spatial component of our larger goal of creating a unique VisitID identifier. Next, we’ll account for the sample dates where SiteID’s within a cluster match.

# Create a date-cluster tibble containing location and event info to be combined,
orig = select(events.3, Date, Cluster, SiteID, EventID, Lat, Lon)

# Nest the df by Date and Cluster,
orig.nest = orig %>%
  group_by(Date, Cluster) %>%
  nest(Sites = SiteID,
       Events = EventID,
       Lats = Lat,
       Lons = Lon) %>%
  ungroup()

We essentially created a our new sample identifier (visit) in nesting by date (day) and cluster (beach). To bring the data back to the EventID level, we need to deal with samples that now have multiple SiteID’s and locations, as well as preserve the EventID’s associated with the original SiteID. This means we will be overwriting data, which is why I called the above subset ‘orig’ and the below ‘new’.

new = orig.nest %>% 
  rowwise() %>% # Operates a row-at-a-time (per sample)
  mutate(SiteID.cl = min(Sites), 
         Lat.mean = mean(Lats$Lat),
         Lon.mean = mean(Lons$Lon)) %>%
  select(-c(Sites, Lats, Lons)) %>% # Remove original info in nested variables
  unite(col = VisitID, SiteID.cl, Date, sep = "_", remove = FALSE) %>% # create VisitID
  select(-c(Date, SiteID.cl, Cluster)) %>% # Remove info now captured by VisitID
  unnest_longer(Events) %>% # Unnest to the EventID level
  unpack(cols = Events) # change tibble back to vector

# Join our new identifier to the rest of the site data,
events = left_join(new, events.3, by = "EventID") %>%
  mutate(Lat = Lat.mean, Lon = Lon.mean) %>% # Replace old lat/lon with new mean lat/lon
  select(-c(Lat.mean, Lon.mean))

# Check our our new data structure
head(events, 12)
## # A tibble: 12 × 17
##    VisitID      EventID SiteID Date         Lat   Lon Region    Location Habitat
##    <chr>          <dbl>  <dbl> <date>     <dbl> <dbl> <chr>     <chr>    <chr>  
##  1 1_2001-07-24      15      1 2001-07-24  58.6 -135. Gulf of … southea… Kelp   
##  2 1_2001-07-24      16      2 2001-07-24  58.6 -135. Gulf of … southea… Kelp   
##  3 1_2001-07-24      17      3 2001-07-24  58.6 -135. Gulf of … southea… Kelp   
##  4 1_2001-07-24      18      4 2001-07-24  58.6 -135. Gulf of … southea… Sand-G…
##  5 1_2001-07-24      19      5 2001-07-24  58.6 -135. Gulf of … southea… Bedrock
##  6 1_2002-03-25      60      1 2002-03-25  58.6 -135. Gulf of … southea… Kelp   
##  7 1_2002-03-25      61      2 2002-03-25  58.6 -135. Gulf of … southea… Kelp   
##  8 1_2002-03-25      62      3 2002-03-25  58.6 -135. Gulf of … southea… Kelp   
##  9 1_2002-03-25      63      4 2002-03-25  58.6 -135. Gulf of … southea… Sand-G…
## 10 1_2002-03-25      64      5 2002-03-25  58.6 -135. Gulf of … southea… Bedrock
## 11 1_2002-03-25      69     10 2002-03-25  58.6 -135. Gulf of … southea… Sand-G…
## 12 1_2002-03-25      70     11 2002-03-25  58.6 -135. Gulf of … southea… Sand-G…
## # ℹ 8 more variables: TidalStage <chr>, Temp_C <dbl>, Salinity <dbl>,
## #   GearSpecific <chr>, ProjectName <chr>, PointOfContact <chr>,
## #   MeshSize <dbl>, Cluster <fct>

We can see in first dozen rows how the locations now structured by VisitID compares to the original based on SiteID. To select an identifier for the new VisitID, I simply went with the lowest SiteID of the nest list (there were no wrong answers here). The lat/lon’s to be combined are at most a handful kms apart, so I figure a simple mean would suffice. Notice we have unique lat/lon’s at the VisitID level- this is because different visits to the same beach might have had a different number of replicates. For example, we see that VisitID 1_2001-07-24 involved the first five events (EventID’s 15-19), whereas, VisitID 1_2002-03-25 had an additional two events.

OK, great. Lastly, let’s return to our frequency plots showing the number events per sample:

Wow. There are way less single-pull samples! Also, there are some interesting samples with replicates of 9, 10, and 12. Some stats on the number of events per visit:

  • mean = 2.53

  • median = 2

  • sd = 1.76

2.7 Clean up

# Clean up our environment for the next step:
rm(list = ls()[!ls() %in% c('events', 'catch.1', 'data')])

# Reset directory
wd = here()
dirs = wd %>% list.files() %>% str_subset(pattern = "^README|^LICENSE|.md$|.Rproj$", negate = TRUE)
for (i in seq_along(dirs)) {
  name = str_replace_all(dirs[i], "^", "dir.")
  path = str_replace_all(dirs[i], "^", str_c(wd, "/"))
  assign(name, path)
  rm(path, i)
}

3 Wrangling catch data

Similar to our wrangle of site information, we want to hone the breadth of the catch data we use. At this stage, we don’t want to make any interpretations about the catch. Instead, the goal here is to weed out objectively unneeded and suspicious cases and ensure the consistency of the data (e.g., are all possible species named actual names?).

3.1 Skimming catch

skim(catch.1)
Data summary
Name catch.1
Number of rows 161621
Number of columns 9
_______________________
Column type frequency:
character 6
numeric 3
________________________
Group variables None

Variable type: character

skim_variable n_missing complete_rate min max empty n_unique whitespace
Sp_CommonName 0 1.00 7 31 0 195 0
Sp_ScientificName 0 1.00 7 38 0 190 0
Fam_CommonName 9 1.00 4 18 0 34 0
Fam_ScientificName 9 1.00 7 18 0 34 0
LengthType 77836 0.52 2 6 0 7 0
LifeStage 90497 0.44 3 8 0 6 0

Variable type: numeric

skim_variable n_missing complete_rate mean sd p0 p25 p50 p75 p100 hist
EventID 0 1.00 6017.42 3244.78 1 3199 6618 9192 10340 ▂▆▁▃▇
Unmeasured 0 1.00 12.80 512.01 0 0 0 0 99997 ▇▁▁▁▁
Length_mm 11535 0.93 98.33 74.16 4 48 76 124 930 ▇▁▁▁▁

One nice thing about skim() is that it quickly tells you where the information is missing and how much is missing. We see that there are nine cases of missing data at the family level. Something else that jumps out to me is that the number of unique species common names is not the same as the number of unique species scientific names.

I’ll also change the ‘Unmeasured’ variable to a count abundance, which may not be totally necessary but will make it easier for me to work with the data.

Another thing to point out is that less than half of the data appears to have life stage information. Furthermore, the data with length information is a little over 50%. We expect some missing lengths because most projects will measure size for a subset of each species and count the rest. Typically, I expect 20-50 individuals sized before a row indicated the unmeasured count, which would result in a very low percentage of “missing” lengths (maybe 5% or less?). But missing lengths over 50% indicate some projects just didn’t measure their catch, which is a shame because we could have inferred life stage from length. At this point, it doesn’t seem worth while to toss out potentially half the data to include either of those variables. Although, I may return to this down the road.

First, let’s check out all the taxa we have in the data.

3.2 Taxa list - before edits

Family

Species

3.3 Juveniles and unidentified fish

Juveniles

Unidentified

Both

catch.1 %>%
  filter(Sp_CommonName %>% str_detect("juvenile|unidentified")) %>%
  select(Fam_ScientificName, Fam_CommonName, Sp_ScientificName, Sp_CommonName) %>%
  distinct() %>%
  arrange(Sp_CommonName) %>%
  datatable(rownames = FALSE,
            extensions = 'Scroller',
            options = list(deferRender = TRUE,
                           scrollY = 500,
                           scroller = TRUE))

3.4 Fixing naming conventions

There is a frustrating mix of so called juveniles that are labelled YOY or Larval or YOY-Larval with inconsistent binning of lengths. For example, we have observations of juvenile sculpin at 9 mm with both YOY and larval labels in the data. The largest juvenile is a greenling at 460 mm. The next largest juvenile is a sculpin at 298 mm. Confusingly, there are also three cases of juvenile sculpin/salmonid that are labelled with adult life stage. Besides that, where fish are unmeasured and no life stage designated, we can probably trust that they are actually juveniles.

When we look at the observations that were labelled ‘unidentified’, we find some more issues to address. Unidentified cases usually come about because fishes were either too small to distinguish in the field (e.g., YOY morphologies), were unfamiliar to the sampler (e.g., rare species), or were taxa that are generally difficult to distinguish based on morphology (e.g., salmonids or cisco). The smallest unidentified fish with an adult life stage label is a 275 mm cisco “Coregonus species”. The largest unidentified fish labelled larval is a 51 mm “Osmeridae or Clupeidae”. This brings up an in naming convention of the unidentified fishes. For example, we have a general ‘unidentified fish’ but also ‘unidentified fish larvae’ and ‘unidentified larval forage fish’. Which brings up another issue that fish are unidentified to differing taxonomic levels- sometimes genus or family or sub-family.

It doesn’t make sense to me to have a juvenile distinction among species names nor have so many unique names among unidentified fish. The simplest fix in my opinion is just to be sure to use scientific names in analyses. We’ll have to address the two cases using “or” in Sp_ScientificName first. I also think we should then remove all unidentified instances that are simply “Division Teleostei” because it is not informative for basic questions of taxonomy. After that, we may wish to aggregate certain groups to higher taxonomy based on how frequent or rarely they occur in the data. So we should create a genus class to preserve that information in case we decide to move to the family level later.

I do want to retain the ’Division Teleostei” data as a subset because I think it could be useful when asking certain questions. I also think we could return to the idea of categorizing species based on life stage by inferring missing information. It would be interesting to examine whether we could predict what those fishes might be based on co-occurring species, time of year, location, etc.

I do not show results of the below code, but I provide an edited taxa list and see also comments for steps taken.

# Fix the "or" cases in Sp_ScientificName
catch.1a = catch.1 %>%
  # Find the 'Anisarchus or Lumpenus' species and replace with Fam_ScientificName
  mutate(Sp_ScientificName = ifelse(str_detect(Sp_ScientificName, pattern = "Anisarchus medius or Lumpenus fabricii"), Fam_ScientificName, Sp_ScientificName),
         # Replace the missing family info for "Osmeridae or Clupidae" with "Division Teloestei" and "bony fishes"
         Fam_CommonName = ifelse(is.na(Fam_CommonName), "bony fishes", Fam_CommonName),
         Fam_ScientificName = ifelse(is.na(Fam_ScientificName), "Division Teleostei", Fam_ScientificName),
         # Create genus class by taking first word of Sp_ScientificName
         Gen_ScientificName = word(Sp_ScientificName, 1))

# Remove all instances of "Division Teleostei" and save the df
catch.2 = filter(catch.1a, Fam_ScientificName != "Division Teleostei")

# Store the removed fishes as .rds in our data folder
filter(catch.1a, Fam_ScientificName == "Division Teleostei") %>%
  saveRDS(., file = file.path(dir.data, "nfa_teleostei.rds"))

3.5 Taxa list - after edits

Family

Genus

Species

3.6 Replacing ‘Unmeasured’ with ‘Count’

The below code changes the ‘Unmeasured’ parameter into ‘Count’ abundance:

catch.3 = mutate(catch.2, Count = ifelse(Unmeasured == 0, 1, Unmeasured)) %>%
  select(-Unmeasured) # remove unused parameter

Next, I’ll make graphs and tables describing the abundance and occurrence of our catch:

3.7 Visualizing catch

Family

Genus

Species

3.8 Defining rare taxa

Removing rare species is common when dealing with community data because of the affect that the most rare species would have in typical analyses to be conducted (e.g., analysis of variance). Depending on our research questions, we may not consider rare cases functional members of the community- in these cases it may make more sense to view the community as those taxa most well represented. But if I were more interested in richness, then I would want to conservatively retain rare taxa.

I think we can convince ourselves that removing single count and even single occurence species would be reasonable, because we do not have any information to let us know whether those cases represent an actual community member or whether they represent some sort of sampling error (e.g., mistakenly labelling all the salmonids in a particular sample Atlantic Salmon rather than a Pacific). Our research questions broadly deal with spatial and temporal distributions of community structure, so I think a ‘middle of the road’ approach is how we’ll want to proceed (at least for now).

We’ll want to be objective in how we define rare taxa. To me, it makes sense to consider occurrence rather than abundance. Our decision should balance the information lost vs retained, and consider how it affects each level of taxonomy. However, I do not know what taxa level makes most sense for future analyses at this point so we’ll want to keep our options open. For now, let’s consider it “rare” when taxa appear in less than 5 total visits (less than 0.25% percent occurrence). With that, removing rare taxa causes:

  • 17.6% reduction in richness at the Family level (6 of 34)

  • 25.6% reduction in richness at the Genus level (30 of 117)

  • 31.4% reduction in richness at the Species Level (59 of 188)

Note that this fixed cut-off affects species diversity more so than family diversity. Some of this can be explained by the fact that the species list contains many identifications at the genus and family level, which makes sense since identification often includes cases where researchers aggregate ‘iffy’ ID’s into a higher class. I expect this issue to be especially apparent in a collaborative dataset like the NFA.

3.9 Lists of rare taxa

Family

Genus

Species

Cool- now let’s re-visit our abundance and occurrence graphs to see how they look:

3.10 Catch after removing rare taxa

Family

Genus

Species

Last thing we’ll do is rename our catch tibble and clean up our environment.

# Re-assign working version of catch to a cleaner name
catch = catch.3

3.11 Clean up

# Clean up our environment for the next step:
rm(list = ls()[!ls() %in% c('data', 'events', 'catch', 'catch.fam', 'catch.gen', 'catch.sp', 'rare.families', 'rare.genus', 'rare.species')])

# Reset directory
wd = here()
dirs = wd %>% list.files() %>% str_subset(pattern = "^README|^LICENSE|.md$|.Rproj$", negate = TRUE)
for (i in seq_along(dirs)) {
  name = str_replace_all(dirs[i], "^", "dir.")
  path = str_replace_all(dirs[i], "^", str_c(wd, "/"))
  assign(name, path)
  rm(path, i)
}

4 Wrangling visit data

4.1 Set up

Remember that our samples operate at the visit level, where unique events represent replicates within each sample. Let’s create a new tibble where each row represents a sample. We’ll want to keep track of how many replicates each sample contains so that we may account for this in future analyses.

# New tibble for visit level wrangling,
visits.1 = select(events, VisitID, EventID) %>%
  summarise(Replicates = n_distinct(EventID), .by = VisitID) %>%
  right_join(select(events, -SiteID, -EventID), by = "VisitID") %>%
  distinct()
visits.1 %>% nrow()
## [1] 3034

4.2 Skimming visits

There are supposed to be 1770 visits, but we see more rows than that in our dataset. When we merged sites together, there must’ve been samples that contained mutliple entries for some of our variables (e.g., more than one Location or Habitat). Let’s skim the data to remind ourselves of our variables:

skim(visits.1)
Data summary
Name visits.1
Number of rows 3034
Number of columns 16
_______________________
Column type frequency:
character 8
Date 1
factor 1
numeric 6
________________________
Group variables None

Variable type: character

skim_variable n_missing complete_rate min max empty n_unique whitespace
VisitID 0 1.00 12 15 0 1770 0
Region 0 1.00 10 16 0 5 0
Location 1 1.00 8 135 0 406 0
Habitat 0 1.00 4 13 0 8 0
TidalStage 694 0.77 3 5 0 6 0
GearSpecific 0 1.00 8 12 0 15 0
ProjectName 0 1.00 4 31 0 31 0
PointOfContact 0 1.00 91 151 0 11 0

Variable type: Date

skim_variable n_missing complete_rate min max median n_unique
Date 0 1 1976-05-29 2021-09-09 2006-06-14 1035

Variable type: factor

skim_variable n_missing complete_rate ordered n_unique top_counts
Cluster 0 1 FALSE 436 140: 203, 143: 161, 136: 114, 348: 113

Variable type: numeric

skim_variable n_missing complete_rate mean sd p0 p25 p50 p75 p100 hist
Replicates 0 1.0 3.27 2.00 1.00 2.00 3.00 4.00 12.00 ▇▂▂▁▁
Lat 0 1.0 59.91 4.07 51.64 58.30 59.42 59.61 71.39 ▁▇▅▁▂
Lon 0 1.0 -145.37 9.93 -176.95 -151.71 -149.65 -134.95 -131.00 ▁▁▇▁▇
Temp_C 1208 0.6 9.84 3.53 -0.92 7.31 10.35 12.47 28.70 ▂▇▇▁▁
Salinity 1218 0.6 24.33 15.97 -400.00 20.18 26.00 30.35 73.50 ▁▁▁▁▇
MeshSize 0 1.0 4.60 2.91 3.00 3.00 3.20 6.00 12.70 ▇▁▁▁▁

Right away, we can rule out the variables that we’ve already dealt with and know are unique at the visit level: VisitID, Lat/Lon, Date, and Cluster.

We can also simply drop the information that we don’t need to keep in this version of the data, namely ProjectName, PointOfContact, and Gear Specific. I think we can also remove the TidalStage parameter, because I don’t care to account for it at this point.

# Update tibble to remove unnecessary site info,
visits.2 = select(visits.1, -c(ProjectName, PointOfContact, GearSpecific, TidalStage)) %>% distinct()
visits.2 %>% nrow()
## [1] 2766

Still not quite there. I bet a lot of visits contain multiple temps, salinity, habitats, and probably locations. We might want to use those later on, but for now we can just drop them because all their info is contained in our event level data object:

# Update tibble to remove 'problem' variables,
visits.3 = select(visits.2, -c(Region, Location, Habitat, Temperature = Temp_C, Salinity)) %>% distinct()
visits.3 %>% nrow()
## [1] 1867

Pretty darn close. Hopefully we don’t have samples with multiple mesh sizes… (spoiler) we do. Yuck.

4.3 The multiple gear-type problem

First, we isolate the cases where singular visits contain replicates of differing mesh sizes:

# New object for visits with multiple mesh size
mult.mesh = select(visits.3, VisitID, MeshSize) %>%
  summarise(`n Mesh Sizes` = n_distinct(MeshSize), .by = VisitID) %>%
  filter(`n Mesh Sizes` > 1)
# View
mult.mesh
## # A tibble: 63 × 2
##    VisitID         `n Mesh Sizes`
##    <chr>                    <int>
##  1 1083_2016-05-07              2
##  2 1083_2016-05-22              2
##  3 1083_2016-06-05              2
##  4 1083_2016-06-21              2
##  5 1083_2016-07-04              2
##  6 1083_2016-08-02              2
##  7 1083_2017-04-11              3
##  8 1083_2016-09-02              2
##  9 1083_2017-04-27              3
## 10 1083_2017-05-14              3
## # ℹ 53 more rows

Damn. Let’s get a better idea of how different these mesh sizes are, and what data sets they come from:

# Join multiple mesh events with other event level info,
mult.mesh.events = select(mult.mesh, VisitID) %>% left_join(events, by = "VisitID")
# Skim the project and date info,
select(mult.mesh.events, GearSpecific, ProjectName, Date) %>%
  mutate(across(c(GearSpecific, ProjectName), ~ as_factor(.x))) %>%
  skim()
Data summary
Name Piped data
Number of rows 382
Number of columns 3
_______________________
Column type frequency:
Date 1
factor 2
________________________
Group variables None

Variable type: Date

skim_variable n_missing complete_rate min max median n_unique
Date 0 1 2016-05-06 2017-09-07 2017-04-30 63

Variable type: factor

skim_variable n_missing complete_rate ordered n_unique top_counts
GearSpecific 0 1 FALSE 3 BSE: 174, BSE: 166, BSE: 42
ProjectName 0 1 FALSE 1 Gla: 382
# Summary of gear and mesh size per visit,
select(mult.mesh.events, VisitID, GearSpecific, MeshSize) %>% 
  summarise(n_visits = n_distinct(VisitID), .by = c(GearSpecific, MeshSize))
## # A tibble: 3 × 3
##   GearSpecific MeshSize n_visits
##   <chr>           <dbl>    <int>
## 1 BSEINE-B          9.5       62
## 2 BSEINE-W         12.7       63
## 3 BSEINE-small      6.4       35

The good thing is that I recognize these data. The visits that contain different mesh size events come from the same project: EPSCoR Fire & Ice Lynn Canal group in Southeast AK. They reported that different mesh sizes had an effect on community composition using side-by-side comparison of gear types in their methods (Lundstrom et al. 2022). This explains why the cluster and grouping steps combined the events.

The bad news is that we’ll need to re-do previous wrangling steps on which mesh size is depoendent. This will involve separating the affected VisitID’s into separate samples based on mesh size, which will result in about 100 more unique VisitID’s and different mean abundance associated catches.

We’ll need to start by updating our events level data object. Since this involves overwriting a major data stage, I’ll be explicit in what I’m doing:

# Create a tibble to store the VisitID's to be fixed
mult.mesh.fix = select(mult.mesh.events, VisitID, EventID, GearSpecific, MeshSize) %>%
  # Create a unique suffix that identifies mesh size used per event
  mutate(suffix = gsub("BSEINE-", "", GearSpecific)) %>%
  # Add the suffix to VisitID, creating new VisitID's
  unite(col = VisitID_new, VisitID, suffix, sep = "_") %>%
  # Retain only the VisitID's and their EventID's
  select(EventID, VisitID_new)

# Update the 'events' tibble with fixed VisitID's,
events = left_join(select(events, EventID, VisitID), mult.mesh.fix, by = "EventID") %>%
  # Where VisitID's were not fixed, fill in with old VisitID's
  mutate(VisitID_new = ifelse(is.na(VisitID_new), VisitID, VisitID_new)) %>%
  # Remove old VisitID column
  select(-VisitID) %>%
  # Re-join events data (w/o the old VisitID's) with the new VisitID's,
  left_join(select(events, -VisitID), by = c("EventID")) %>%
  # Rename (this could be done earlier but seems clearer here)
  rename(VisitID = VisitID_new)

Next, we need to re-run the catch and visits steps that are based on the events data.

4.4 Re-creating catch summaries

We’ll need to redo the abundance and occurrence calculations because the number of visits has changed. Rare taxa will still be defined as those occurring in less than 5 visits (still equivalent to <0.25%). The frequency and abundance plots are not all that different, but I show updated catch summary tables below for each taxa level. Note that I highlight rare taxa in orange.

Family

Genus

Species

Now we can re-run the VisitID wrangling code from earlier in this section:

# Re-run visits.1 code,
visits.4 = select(events, VisitID, EventID) %>%
  summarise(Replicates = n_distinct(EventID), .by = VisitID) %>%
  right_join(select(events, -SiteID, -EventID), by = "VisitID") %>%
  distinct() %>%
  # Re-run visits.2 & visits.3 code,
  select(-c(ProjectName, PointOfContact, GearSpecific, TidalStage,
            Region, Location, Habitat, Temperature = Temp_C, Salinity)) %>%
  distinct()

4.5 Re-visiting sample frequency

We now have 1867 unique visits. Let’s double check that there is only one row per VisitID:

n_distinct(visits.4$VisitID) == nrow(visits.4)
## [1] TRUE

Let’s also review our events per visit frequency plot because that will have changed too.

The difference isn’t very noticeable- we end up with a central tendency that has slightly shifted left.

Last, we save our fixed visits tibble as a new object with a cleaner name, and then clean up the environment for next steps.

visits = visits.4

5 Clean up and save work

# Remove unwanted objects
rm(list = ls()[!ls() %in% c('data', 'events', 'catch', 'visits',
                            'fam.tbl', 'gen.tbl', 'sp.tbl')])

# Check sizes of the data objects
eapply(as.environment(-1), FUN = function(x) format(object.size(x), units = "Mb"))
## $gen.tbl
## [1] "0 Mb"
## 
## $data
## [1] "42.3 Mb"
## 
## $fam.tbl
## [1] "0 Mb"
## 
## $sp.tbl
## [1] "0 Mb"
## 
## $visits
## [1] "0.2 Mb"
## 
## $catch
## [1] "12.3 Mb"
## 
## $events
## [1] "0.8 Mb"
# The original 'data' import is quite large,
# but also unneeded bc the info is held in other objects, so let's remove it:
rm('data')

# Save what's left,
save(list = ls(), file = file.path(here(), "data", "NFA_wrangled.rda"))
LS0tCnRpdGxlOiAiTk9BQSBOZWFyc2hvcmUgRmlzaCBBdGxhcywgRGF0YSBXcmFuZ2xlIgphdXRob3I6ICJDaHJpcyBHdW8iCmRhdGU6ICJMYXN0IGNvbXBpbGVkIG9uIGByIGZvcm1hdChTeXMudGltZSgpLCAnJWQgJUIsICVZJylgIgpvdXRwdXQ6CiAgaHRtbF9kb2N1bWVudDoKICAgIHRvYzogVFJVRQogICAgdG9jX2RlcHRoOiAyCiAgICB0b2NfZmxvYXQ6CiAgICAgIGNvbGxhcHNlZDogRkFMU0UKICAgICAgcHJpbnQ6IEZBTFNFCiAgICBudW1iZXJfc2VjdGlvbnM6IFRSVUUKICAgIGNvZGVfZG93bmxvYWQ6IFRSVUUKdGhlbWU6ICJmbGF0bHkiCi0tLQoKYGBge3IgaW5jbHVkZSA9IEZBTFNFfQprbml0cjo6b3B0c19jaHVuayRzZXQobWVzc2FnZSA9IEZBTFNFKQprbml0cjo6b3B0c19jaHVuayRzZXQod2FybmluZyA9IEZBTFNFKQprbml0cjo6b3B0c19jaHVuayRzZXQoc2l6ZSA9ICJzY3JpcHRzaXplIikKYGBgCgpCdWlsdCB3aXRoIFIgdmVyc2lvbiBgciBnZXRSdmVyc2lvbigpYC4KCiMgSW50cm9kdWN0aW9uCgpUaGUgcG9pbnQgb2YgdGhpcyBkb2N1bWVudCBpcyB0byBtYWtlIG9wZW4gYW5kIHNoYXJlLWFibGUgdGhlIHJlc2VhcmNoIG1ldGhvZHMgdXNlZCBmb3IgbXkgZGlzc2VydGF0aW9uIG9uIG5lYXJzaG9yZSBmaXNoIGNvbW11bml0aWVzIGluIEFsYXNrYSB3b3JraW5nIHRvd2FyZHMgYSBQaEQgaW4gbWFyaW5lIGJpb2xvZ3kgYXQgdGhlIFVuaXZlcnNpdHkgb2YgQWxhc2thIEZhaXJiYW5rcy4gSGVyZSwgSSBjb3ZlciBtb3N0IHN0ZXBzIG9mIHRoZSBpbml0aWFsIGRhdGEgcHJlcGFyYXRpb24gZm9yIG15IHNlY29uZCBhbmQgdGhpcmQgY2hhcHRlcnMgY29uY2VybmluZyBzcGF0aWFsIGFuZCB0ZW1wb3JhbCBkaXN0cmlidXRpb25zIG9mIG5lYXJzaG9yZSBmaXNoZXMgYWNyb3NzIHRoZSBzdGF0ZS4gVGhpcyBhbmQgb3RoZXIgcHJvamVjdCBmaWxlcyBjYW4gYmUgYWNjZXNzZWQgdmlhIHRoZSBLYWNoZW1hayBCYXkgTmF0aW9uYWwgRXN0dWFyaW5lIFJlc2VhcmNoIFJlc2VydmUncyBnaXRodWIgW25lYXJzaG9yZSBmaXNoIHJlcG9zaXRvcnldKGh0dHBzOi8vZ2l0aHViLmNvbS9rYm5lcnIvbmVhcnNob3JlLWZpc2gpLiBGb2xsb3dpbmcgdGhpcyBkb2N1bWVudCwgbmV4dCBzdGVwIGV4cGxvcmF0b3J5IGRhdGEgYW5hbHlzZXMgcmVmZXJlbmNpbmcgdGhlc2UgZGF0YSBjYW4gYmUgZm91bmQgYXQgW3RoaXMgUnB1YnMgc2l0ZV0oaHR0cHM6Ly9ycHVicy5jb20vY2hndW8xLzEyMzI2NzMpLgoKSW4gdGhpcyBkb2N1bWVudCBJIHNob3cgdGhlIHN0ZXBzIHRha2VuIGluIHdyYW5nbGluZyB0aGUgTk9BQSBOZWFyc2hvcmUgRmlzaCBBdGxhcyAoTkZBKSBkYXRhYmFzZS4gVGhpcyByZXBsYWNlcyB0aGUgc2VwYXJhdGUgc2NyaXB0cyAobmFtZWQgIi4vRmlzaEF0bGFzXF8jXF9kZXNjcmlwdGlvbi5SIikgYW5kIGNvbWJpbmVzIHRoZW0gaW50byBhIHNpbmdsZSwgc3RyZWFtbGluZWQgUiBtYXJrZG93biBmaWxlLiBUaGVzZSBSIHNjcmlwdHMgd2lsbCBiZSBhcmNoaXZlZCBpbiBjYXNlIEkgbmVlZCB0byByZXBsaWNhdGUgb2xkZXIgbWV0aG9kcy4gTmFtZWx5LCBJIHVzZWQgdG8gdXNlICoqcmdkYWwqKiBwYWNrYWdlIGZvciBzb21lIHN0ZXBzIGluIHRoZSB3cmFuZ2xpbmcgcHJvY2VzcyBidXQgdGhhdCBwYWNrYWdlIHN0b3BwZWQgYmVpbmcgc3VwcG9ydGVkIGNhLiBPY3QgMjAyMywgbW92aW5nIG15IHdvcmtmbG93IHRvIHRoZSAqKnNmKiogcGFja2FnZS4KCkxpbmsgdG8gdGhlIE5GQSBjYW4gYmUgZm91bmQgaGVyZSwgPGh0dHBzOi8vYWxhc2thZmlzaGVyaWVzLm5vYWEuZ292L21hcHBpbmcvc3ovPi4gVGhlIGVudGlyZSBkYXRhYmFzZSBjYW4gYmUgZG93bmxvYWRlZCBhcyBhIC5jc3YgZmlsZSwgd2hpY2ggaXMgd2hhdCBJIGFtIHVzaW5nLiBIb3dldmVyLCBJIGhhdmUgYWRkaXRpb25hbCBkYXRhIGZpbGVzIHRoYXQgd2VyZSBlaXRoZXIgc2hhcmVkIHdpdGggbWUgYnkgTkZBIGNvbnRlbnQgbWFuYWdlcnMgb3IgbW9kaWZpZWQgZnJvbSB0aGUgTk9BQSBORkEgZGF0YSBkaXJlY3RvcnkgZm9yIGVhc2Ugb2YgcmVhZGluZyBpbnRvIFIuIEkgd2lsbCB0cnkgdG8gc2ltcGxpZnkgdGhpcyBmb3Igb3RoZXJzIHRvIHJlcGxpY2F0ZSBpbiB0aGUgZnV0dXJlLgoKIyBXcmFuZ2xpbmcgc2l0ZSBkYXRhCgpUaGUgaWRlYSB3aXRoIHRoaXMgaW5pdGlhbCBzdGVwIGlzIHRvIGRlZmluZSB0aGUgc2NhbGUgb2Ygb3VyIHNhbXBsZXMuIENvbnRyaWJ1dG9ycyB0byB0aGUgTkZBIGRpZCBub3QgZGVmaW5lIHRoZWlyIFNpdGVJRCdzIGFuZCBFdmVudElEJ3Mgc2ltaWxhciB0byBlYWNoIG90aGVyLCBzbyB3ZSdsbCBuZWVkIHRvIGRpc3Rpbmd1aXNoIGJldHdlZW4gU2l0ZUlEIHZzIEV2ZW50SUQgYW5kIG1ha2Ugc3VyZSBpdCdzIGNvbnNpc3RlbnQgdGhyb3VnaG91dCB0aGUgZGF0YS4KCkkgZGVmaW5lIGEgU2l0ZUlEIGFzICphIHVuaXF1ZSBwbGFjZSBpbiBzcGFjZSosIGkuZS4sIGFsbCBzYW1wbGVzIGZyb20gdGhlIHNhbWUgJ2JlYWNoJyAoJ2JlYWNoJyB3aWxsIGJlIGRlZmluZWQgbGF0ZXIgb24pLiBBbiBFdmVudElEIGlzICphIHVuaXF1ZSBwdWxsIG9mIHRoZSBzZWluZSouIFNvIHRoZXJlIGNhbiBiZSBzaW5nbGUgb3IgbXVsdGlwbGUgRXZlbnRJRCdzIGFzc29jaWF0ZWQgd2l0aCBhIFNpdGVJRC4gQWRkaXRpb25hbGx5LCBJIGludHJvZHVjZSBhIFZpc2l0SUQsIHdoaWNoIGFkZHMgYSB0ZW1wb3JhbCBkaXN0aW5jdGlvbiBhbW9uZyBTaXRlSUQncywgaS5lLiwgKmFsbCBldmVudHMgZnJvbSB0aGUgc2FtZSBiZWFjaCBvbiB0aGUgc2FtZSBkYXkqLgoKIyMgU2V0IHVwCgpMb2FkIHJlcXVpcmVkIHBhY2thZ2VzLCBkZWZpbmUgZGlyZWN0b3J5LCBzZXQgb3B0aW9ucywgc291cmNlIHNjcmlwdHM6CgpgYGB7cn0KIyBQYWNrYWdlcwpsaWJyYXJ5KHRpZHl2ZXJzZSkKbGlicmFyeShsdWJyaWRhdGUpCmxpYnJhcnkoaGVyZSkKbGlicmFyeShza2ltcikKbGlicmFyeShzZikKbGlicmFyeShEVCkKCiMgRGlyZWN0b3J5CndkID0gaGVyZSgpCmRpcnMgPSB3ZCAlPiUgbGlzdC5maWxlcygpICU+JSBzdHJfc3Vic2V0KHBhdHRlcm4gPSAiXlJFQURNRXxeTElDRU5TRXwubWQkfC5ScHJvaiQiLCBuZWdhdGUgPSBUUlVFKQpmb3IgKGkgaW4gc2VxX2Fsb25nKGRpcnMpKSB7CiAgbmFtZSA9IHN0cl9yZXBsYWNlX2FsbChkaXJzW2ldLCAiXiIsICJkaXIuIikKICBwYXRoID0gc3RyX3JlcGxhY2VfYWxsKGRpcnNbaV0sICJeIiwgc3RyX2Mod2QsICIvIikpCiAgYXNzaWduKG5hbWUsIHBhdGgpCiAgcm0obmFtZSwgcGF0aCwgaSkKfQoKIyBPcHRpb25zCgojIFNvdXJjZQoKYGBgCgojIyBSZWFkIGluIGRhdGEKCmBgYHtyfQpkYXRhID0gcmVhZF9jc3YoZmlsZS5wYXRoKGRpci5kYXRhLCAiUmF3IEZpc2ggQXRsYXMgU2l0ZXMgMjAyNC0xMC0xNi5jc3YiKSwKICAgICAgICAgICAgICAgc2hvd19jb2xfdHlwZXMgPSBGQUxTRSkKR2Vhckxvb2t1cCA9IHJlYWRfY3N2KGZpbGUucGF0aChkaXIuZGF0YSwgIkZpc2hBdGxhc0V4cGFuc2lvbl8wNDA0MjJfTG9va3VwX0dlYXIuY3N2IiksCiAgICAgICAgICAgICAgICAgICAgICBzaG93X2NvbF90eXBlcyA9IEZBTFNFKQpgYGAKCiMjIFNwbGl0dGluZyBzaXRlIGFuZCBzcGVjaWVzIGluZm8KCkFzIGluIHR5cGljYWwgY29tbXVuaXR5IGRhdGEgKHNpdGUgeCBzcGVjaWVzKSwgd2UgaGF2ZSBpbmZvIHRpZWQgdG8gc2l0ZSBkZXNjcmlwdGlvbiBhbmQgaW5mbyB0aWVkIHRvIHRheGEuIE5vdGljZSB0aGF0IEV2ZW50SUQgaXMgdGhlICdsb29rLXVwJyBrZXkgdXNlZCBpbiBib3RoIGRhdGEgc2V0cy4KCmBgYHtyfQpldmVudHMuMSA9IGRhdGEgJT4lCiAgc2VsZWN0KFNpdGVJRCwgRXZlbnRJRCwKICAgICAgICAgRGF0ZSwKICAgICAgICAgTGF0LCBMb24gPSBMb25nLCBSZWdpb24sIExvY2F0aW9uLAogICAgICAgICBIYWJpdGF0LCBUaWRhbFN0YWdlLCBUZW1wX0MsIFNhbGluaXR5LAogICAgICAgICBHZWFyU3BlY2lmaWMsIFByb2plY3ROYW1lLCBQb2ludE9mQ29udGFjdCkgJT4lCiAgbXV0YXRlKERhdGUgPSBtZHkoRGF0ZSkpICU+JSAjIHJlLWZvcm1hdCBkYXRlCiAgZGlzdGluY3QoKQpnbGltcHNlKGV2ZW50cy4xKQoKY2F0Y2guMSA9IGRhdGEgJT4lCiAgc2VsZWN0KEV2ZW50SUQsCiAgICAgICAgIFNwX0NvbW1vbk5hbWUsIFNwX1NjaWVudGlmaWNOYW1lLCBGYW1fQ29tbW9uTmFtZSwgRmFtX1NjaWVudGlmaWNOYW1lLAogICAgICAgICBVbm1lYXN1cmVkLCBMZW5ndGhfbW0sIExlbmd0aFR5cGUsIExpZmVTdGFnZSkKZ2xpbXBzZShjYXRjaC4xKQpgYGAKCiMjIENvbnNpZGVyaW5nIGdlYXIgdHlwZQoKVGhlIE5GQSB3ZWJzaXRlIG1ha2VzIGl0IGVhc3kgdG8gZmlsdGVyIGZvciBnZWFyIHR5cGUsIHdoaWNoIEkgZGlkIHByaW9yIHRvIGRvd25sb2FkaW5nIHRoZSAuY3N2IGZpbGUuIEhvd2V2ZXIsIHdlJ2xsIHdhbnQgdG8gYWNjb3VudCBmb3IgZGlmZmVyZW5jZXMgYW1vbmcgdGhlIGJlYWNoIHNlaW5lcyB1c2VkLSBuYW1lbHkgdGhlIG5ldCBtZXNoIHNpemUuIFRoaXMgaW5mb3JtYXRpb24gaXMgY29udGFpbmVkIGluIHRoZSBtZXRhZGF0YSBmb3IgdGhlIE5GQSBkYXRhYmFzZSBhbmQgY2FuIGJlIGFjY2Vzc2VkIGJ5IHJlcXVlc3QgZnJvbSB0aGUgZGF0YWJhc2UgbWFuYWdlcnMgKGZpbGUgKkZpc2hBdGxhc0V4cGFuc2lvbl8wNDA0MjJfTG9va3VwX0dlYXIuY3N2KikuIEEgZmV3IGluc3RhbmNlcyBvZiBtaXNzaW5nIG1lc2ggc2l6ZSBpbmZvcm1hdGlvbiB3YXMgcHJvdmlkZWQgYnkgZW1haWwgcmVxdWVzdCB0byB0aGUgYXNzb2NpYXRlZCBwb2ludCBvZiBjb250YWN0IGZvciB0aGF0IGNvbnRyaWJ1dGVkIGRhdGEuIFJlc3VsdHMgZnJvbSB0aGUgYmVsb3cgY29kZSBhcmUgaGlkZGVuIGJ1dCBjb21tZW50cyBleHBsYWluIHdoYXQgZGVjaXNpb25zIHdlcmUgbWFkZS4KCmBgYHtyIHJlc3VsdHMgPSAnaGlkZSd9CiMgSm9pbiB0aGUgbWVzaCBzaXplIGluZm8gZnJvbSBvdXIgZ2VhciBkYXRhIHRvIHRoZSBzaXRlIGluZm9ybWF0aW9uCmdlYXIuMSA9IGxlZnRfam9pbihldmVudHMuMSwgc2VsZWN0KEdlYXJMb29rdXAsIEdlYXJTcGVjaWZpYywgTWVzaFNpemUpLCBieSA9ICJHZWFyU3BlY2lmaWMiKQoKIyBTdWJzZXQgZm9yIGdlYXIgcmVsYXRlZCBpbmZvIGFuZCB0YWtlIGEgbG9vawpnZWFyLjIgPSBzZWxlY3QoZ2Vhci4xLCBFdmVudElELCBNZXNoU2l6ZSwgR2VhclNwZWNpZmljLCBQcm9qZWN0TmFtZSwgUG9pbnRPZkNvbnRhY3QpCmdsaW1wc2UoZ2Vhci4yKQojIEhvdyBtYW55IGRpZmZlcmVudCBtZXNoIHNpemVzIGFyZSB0aGVyZT8KZ2Vhci4yJE1lc2hTaXplICU+JSB1bmlxdWUoKQoKIyBMb29rcyBsaWtlIHdlIGhhdmUgYW4gTkEgdG8gdGFrZSBjYXJlIG9mLi4uCmZpbHRlcihnZWFyLjIsIGlzLm5hKE1lc2hTaXplKSkKIyBUaGVzZSBhcmUgYWxsIGZyb20gdGhlIEdPQUlFUlAgcHJvamVjdCAoUEkgT2xhdiBPcm1zZXRoKS4gVGhlIHJlcG9ydCBmcm9tIHRoYXQgcHJvamVjdCByZXBvcnRzIDYgbW0gc3RyZXRjaGVkIG1lc2guCiMgV2UnbGwganVzdCBnbyBhaGVhZCBhbmQgZml4IGl0IGhlcmUgaW5zdGVhZCBvZiBjaGFuZ2luZyB0aGUgR2Vhckxvb2t1cCBmaWxlLApnZWFyLjMgPSBtdXRhdGUoZ2Vhci4yLCBNZXNoU2l6ZSA9IGlmZWxzZShpcy5uYShNZXNoU2l6ZSksIDYsIE1lc2hTaXplKSkKCiMgQ2hlY2sgb3VyIG1lc2ggc2l6ZXMgYWdhaW4sCmdlYXIuMyRNZXNoU2l6ZSAlPiUgdW5pcXVlKCkKIyBHcmVhdCwgbm8gbW9yZSBOQSdzLgoKIyBXZSBjYW4gbm93IGpvaW4gdGhpcyBpbmZvcm1hdGlvbiB3aXRoIG91ciBzaXRlIGRhdGEsCmV2ZW50cy4yID0gbGVmdF9qb2luKGV2ZW50cy4xLCBzZWxlY3QoZ2Vhci4zLCBFdmVudElELCBNZXNoU2l6ZSksIGJ5ID0gIkV2ZW50SUQiKQpgYGAKCiMjIFRoZSAiU2l0ZS1FdmVudCIgcHJvYmxlbQoKVGhlIGNhdGNoIGRhdGEgaXMgdGllZCB0byB1bmlxdWUgRXZlbnRJRCdzIHdoaWNoIGFyZSBhc3NvY2lhdGVkIHdpdGggc3BhdGlhbGx5IGV4cGxpY2l0IFNpdGVJRCdzIChlYWNoIFNpdGVJRCBoYXMgYSB1bmlxdWUgbGF0L2xvbikuIENvbnNpZGVyaW5nIHRoYXQgdGhlIGRhdGEgaGFzIGJlZW4gY29udHJpYnV0ZWQgYnkgbXVsdGlwbGUgcmVzZWFyY2hlcnMvcHJvamVjdHMsIHdlJ2xsIG5lZWQgdG8gbWFrZSBzdXJlIHdlIGhhdmUgY29uc2lzdGVudCBzaXRlcyBhbmQgZXZlbnRzIGFjcm9zcyB0aGUgd2hvbGUgZGF0YXNldC4gQWZ0ZXIgdGhhdCB3ZSBjYW4gdGFja2xlIHRoZSBjYXRjaCBkYXRhLiBGaXJzdCwgd2UgdGFrZSBhIGxvb2sgYXQgYWxsIG9mIHRoZSBzaXRlIGRhdGEgd2l0aCAqKnNraW1yOjpza2ltKCkqKi4KCmBgYHtyfQpza2ltKGV2ZW50cy4yKQpgYGAKCkluIHBhcnRpY3VsYXIgd2Ugd2FudCB0byBnZXQgYSBzZW5zZSBvZiBob3cgRXZlbnRJRHMgYXJlIGFzc29jaWF0ZWQgd2l0aCBTaXRlSURzIGFuZCBvdGhlciB2YXJpYWJsZXMgcGxhY2luZyB0aGVtIGluIHNwYWNlIGFuZCB0aW1lLgoKYGBge3IgZWNobyA9IEZBTFNFfQpldmVudHMuMiAlPiUgc3VtbWFyaXNlKEV2ZW50cyA9IG5fZGlzdGluY3QoRXZlbnRJRCksIC5ieSA9IGMoU2l0ZUlELCBEYXRlKSkgJT4lCiAgZ2dwbG90KGRhdGEgPSAuLCBhZXMoeCA9IEV2ZW50cykpICsKICBnZW9tX2JhcihhZXMoeSA9IGFmdGVyX3N0YXQoY291bnQpKSkgKwogIGdlb21fdGV4dChzdGF0ID0gJ0NvdW50JywgYWVzKGxhYmVsID0gYWZ0ZXJfc3RhdChjb3VudCkpLCB2anVzdCA9IC0wLjUpICsKICBsYWJzKHRpdGxlID0gIkZyZXF1ZW5jeSBvZiBldmVudHMgKHJlcGxpY2F0ZXMpIHBlciBTaXRlSUQtRGF0ZSBwYWlyIChzYW1wbGVzKSIpCgpldmVudHMuMiAlPiUgc3VtbWFyaXNlKEV2ZW50cyA9IG5fZGlzdGluY3QoRXZlbnRJRCksIC5ieSA9IGMoRGF0ZSwgTG9jYXRpb24pKSAlPiUKICBnZ3Bsb3QoZGF0YSA9IC4sIGFlcyh4ID0gRXZlbnRzKSkgKwogIGdlb21fYmFyKGFlcyh5ID0gYWZ0ZXJfc3RhdChjb3VudCkpKSArCiAgZ2VvbV90ZXh0KHN0YXQgPSAnQ291bnQnLCBhZXMobGFiZWwgPSBhZnRlcl9zdGF0KGNvdW50KSksIHZqdXN0ID0gLTAuNSkgKwogIGxhYnModGl0bGUgPSAiRnJlcXVlbmN5IG9mIGV2ZW50cyBwZXIgTG9jYXRpb24tRGF0ZSBwYWlyIikKYGBgCgpXZSBzZWUgYSBsb3Qgb2Ygc2luZ2xlLXB1bGwgc2FtcGxlcyB3aGVuIGV2ZW50cyBhcmUgZ3JvdXBlZCBieSBTaXRlSUQgYW5kIERhdGUsIGkuZS4sIGEgKnZpc2l0Ki4gSG93ZXZlciwgd2UgY2FuJ3QgYXNzdW1lIGFsbCBvZiB0aG9zZSAndmlzaXRzJyBhcmUgY29tcGFyYWJsZSBpbiBzY29wZS4gUHJvamVjdHMgZGlmZmVyZWQgaW4gc3RydWN0dXJlIGFuZCBwdXJwb3NlLCBzbyB3aGF0IG9uZSByZXNlYXJjaGVyIHdvdWxkIGNvbnNpZGVyIGEgbmV3IHNpdGUsIGFub3RoZXIgcmVzZWFyY2hlciBtaWdodCBjYWxsIGEgcmVwbGljYXRlIGV2ZW50IHRvIGJlIGNvbWJpbmVkIHdpdGggb3RoZXIgZXZlbnRzIHdpdGhpbiBhIHNpbmdsZSBzYW1wbGUuIEkgY2FsbCB0aGlzIHRoZSAiU2l0ZS1FdmVudCIgcHJvYmxlbS4KCldlIHNlZSB0aGF0IHRoZSBkaXN0cmlidXRpb24gb2YgZXZlbnRzIGJlY29tZXMgbGVzcyBzZXZlcmUgd2hlbiBncm91cGVkIGJ5IERhdGUgYW5kIExvY2F0aW9uLCB3aGljaCB0ZWxscyBtZSB0aGF0IHNvbWUgb2YgdGhvc2Ugc2luZ2xlLXB1bGwgc2FtcGxlcyBhY3R1YWxseSBzaG91bGQgYmUgYWdncmVnYXRlZCB0byBiZSBjb21wYXJhYmxlIHRvIG90aGVyIG11bHRpLXB1bGwgc2FtcGxlcy4gSG93ZXZlciwgZ3JvdXBpbmcgYnkgbG9jYXRpb24gbWF5IG5vdCBiZSBhY2N1cmF0ZSBiZWNhdXNlIG9mIHRoZSBsb29zZSBkZWZpbml0aW9uIG9mICdMb2NhdGlvbicgKHRoZSBsZXZlbHMgb2YgTG9jYXRpb24gYWxzbyBzZWVtIHRvIHZhcnkgaW4gc2NhbGUpLiBTaW5jZSBsb2NhdGlvbiBpbmZvIGlzIHRpZWQgdG8gU2l0ZUlELCB3ZSBjYW4ndCBqdXN0IGxvb2sgYXQgRXZlbnRJRCdzIGFuZCBsYXQvbG9uLiBJbnN0ZWFkLCB3ZSdsbCBuZWVkIHRvIGZpZ3VyZSBvdXQgYSB3YXkgdG8gY29tYmluZSBldmVudHMgYXQgdGhlIFNpdGVJRCBsZXZlbC4gQmFzaWNhbGx5LCB3ZSBhcmUgdHJ5aW5nIHRvIGNvbWJpbmUgU2l0ZUlEJ3MgdGhhdCBhcmUgY3VycmVudGx5IHVuaXF1ZSBidXQgc2hvdWxkIGhhdmUgdGhlIHNhbWUgaWRlbnRpZmllciwgaS5lLiwgc2FtcGxlcyBvZiB0aGUgc2FtZSBiZWFjaC4KCiMjIFNvbHZpbmcgdGhlICJTaXRlLUV2ZW50IiBwcm9ibGVtCgpIZXJlIGlzIHdoZXJlIHdlIG5lZWQgdG8gZGVjaWRlIG9uIGhvdyB0byBkZWZpbmUgYSAnYmVhY2gnLiBJbiBvdGhlciB3b3Jkcywgd2hhdCBkbyB3ZSBjb25zaWRlciB0aGUgc3BhdGlhbCBzY2FsZSBkaXN0aW5ndWlzaGluZyBvbmUgc2FtcGxlIGZyb20gYW5vdGhlcj8gRnJvbSBteSBwZXJzb25hbCBzZWluaW5nLCBvbmUgb2YgbXkgbGFyZ2VyLWRpc3RhbmNlZCBzaXRlcyB3YXMgbmVhciBBbmNob3IgUG9pbnQsIEFLLCB3aGVyZSB3ZSd2ZSBzZWluZWQgdXAgdG8gODAwIG0gYmV0d2VlbiBmaXJzdCBhbmQgbGFzdCByZXBsaWNhdGUuIFNvIGluIG15IG9waW5pb24sIDEga20gd291bGQgYmUgcmVhc29uYWJseSBjb25zZXJ2YXRpdmUgZXN0aW1hdGUgdG8gbGluayByZXBsaWNhdGUgc2VpbmVzLiBJbWFnaW5lIHNlaW5pbmcgYnkgc2tpZmYtIHlvdSBjb3VsZCBob3AgYmV0d2VlbiByZXBsaWNhdGVzIFx+MSBrbSBkb3duIGJlYWNoIGFuZCBzdGlsbCBjb25zaWRlciBpdCB0aGUgc2FtZSBzaXRlLiBJbiB0aGUgYmVsb3cgY29kZSwgSSBkZWZpbmUgY2x1c3RlcnMgb2YgU2l0ZUlEJ3MgYnkgZ3JvdXBpbmcgcG9pbnRzIHRoYXQgYXJlIHdpdGhpbiAxMDAwIG0gb2YgYXQgbGVhc3Qgb25lIG90aGVyIFNpdGVJRC4KCmBgYHtyfQojIE1ha2UgYSBuZXcgdGliYmxlIGNvbnRhaW5pbmcgU2l0ZUlEIGdlb21ldHJpZXM6CnNpdGVzLnNmID0gZXZlbnRzLjIgJT4lCiAgc2VsZWN0KFNpdGVJRCwgTGF0LCBMb24pICU+JQogICMgQ3JlYXRlIHNmIG9iamVjdCBmcm9tIGxhdC9sb246CiAgc3RfYXNfc2YoLiwgY29vcmRzID0gYygiTG9uIiwgIkxhdCIpLCBjcnMgPSA0MzI2KSAlPiUgIyBXR1M4NAogICMgUHJvamVjdCBpbnRvIEFsYXNrYSBBbGJlcnMgMzMzOAogIHN0X3RyYW5zZm9ybShjcnMgPSAzMzM4KSAlPiUKICBkaXN0aW5jdCgpCgojIERlZmluZSBhIGJ1ZmZlciBvZiAxa20gYXJvdW5kIGVhY2ggcG9pbnQKc2l0ZXMuc2YuYnVmID0gc3RfYnVmZmVyKHNpdGVzLnNmLCBkaXN0ID0gMTAwMCkKCiMgQ3JlYXRlIGEgdGliYmxlIHdpdGggY2x1c3RlcnMgb2Ygb3ZlcmxhcHBpbmcgYnVmZmVyIGFyZWFzIApzaXRlcy5zZi5jbCA9IHNpdGVzLnNmLmJ1ZiAlPiUKICBzdF91bmlvbigpICU+JSAjIHVuaXRlIGJ1ZmZlciBhcmVhcwogIHN0X2Nhc3QodG8gPSAiUE9MWUdPTiIpICU+JSAjIHR1cm4gdGhlbSBpbnRvIHBvbHlnb24gZmVhdHVyZXMKICBzdF9hc19zZigpICU+JSAjIHJldHVybiBnZW9tZXRyeSBzZXQgZmVhdHVyZXMgdG8gc2ZjCiAgcm93bmFtZXNfdG9fY29sdW1uKHZhciA9ICJDbHVzdGVyIikgJT4lICMgY3JlYXRlIGNvbCBmb3IgY2x1c3RlciBncm91cHMKICBtdXRhdGUoQ2x1c3RlciA9IGFzLmZhY3RvcihDbHVzdGVyKSkgIyB1c2UgZmFjdG9yIGNsYXNzCgojIEFzc2lnbiBTaXRlSUQgcG9pbnRzIHRvIGJ1ZmZlciBhcmVhIGNsdXN0ZXJzCnNpdGVzLnNmLmNsLmpvaW4gPSBzdF9qb2luKHNpdGVzLnNmLCBzaXRlcy5zZi5jbCwgbGVmdCA9IFRSVUUpCgojIERyb3Agc2YgY2xhc3MgZnJvbSBqb2luZWQgZGF0YQpzaXRlcy5jbCA9IHN0X2Ryb3BfZ2VvbWV0cnkoc2l0ZXMuc2YuY2wuam9pbikKCiMgU2VlIHdoYXQgd2UgZ290OgpnbGltcHNlKHNpdGVzLmNsKQpgYGAKCldlIG5vdyBoYXZlIGEgc2ltcGxlIGNsYXNzaWZpY2F0aW9uIHZhcmlhYmxlIHRoYXQgY2FuIGJlIGpvaW5lZCBiYWNrIHdpdGggdGhlIHNpdGUgaW5mb3JtYXRpb24uCgpgYGB7cn0KZXZlbnRzLjMgPSBsZWZ0X2pvaW4oZXZlbnRzLjIsIHNpdGVzLmNsLCBieSA9ICJTaXRlSUQiKQpgYGAKCkhvd2V2ZXIsIHRoaXMgb25seSB0YWtlcyBpbnRvIGFjY291bnQgdGhlIHNwYXRpYWwgY29tcG9uZW50IG9mIG91ciBsYXJnZXIgZ29hbCBvZiBjcmVhdGluZyBhIHVuaXF1ZSBWaXNpdElEIGlkZW50aWZpZXIuIE5leHQsIHdlJ2xsIGFjY291bnQgZm9yIHRoZSBzYW1wbGUgZGF0ZXMgd2hlcmUgU2l0ZUlEJ3Mgd2l0aGluIGEgY2x1c3RlciBtYXRjaC4KCmBgYHtyfQojIENyZWF0ZSBhIGRhdGUtY2x1c3RlciB0aWJibGUgY29udGFpbmluZyBsb2NhdGlvbiBhbmQgZXZlbnQgaW5mbyB0byBiZSBjb21iaW5lZCwKb3JpZyA9IHNlbGVjdChldmVudHMuMywgRGF0ZSwgQ2x1c3RlciwgU2l0ZUlELCBFdmVudElELCBMYXQsIExvbikKCiMgTmVzdCB0aGUgZGYgYnkgRGF0ZSBhbmQgQ2x1c3RlciwKb3JpZy5uZXN0ID0gb3JpZyAlPiUKICBncm91cF9ieShEYXRlLCBDbHVzdGVyKSAlPiUKICBuZXN0KFNpdGVzID0gU2l0ZUlELAogICAgICAgRXZlbnRzID0gRXZlbnRJRCwKICAgICAgIExhdHMgPSBMYXQsCiAgICAgICBMb25zID0gTG9uKSAlPiUKICB1bmdyb3VwKCkKYGBgCgpXZSBlc3NlbnRpYWxseSBjcmVhdGVkIGEgb3VyIG5ldyBzYW1wbGUgaWRlbnRpZmllciAodmlzaXQpIGluIG5lc3RpbmcgYnkgZGF0ZSAoZGF5KSBhbmQgY2x1c3RlciAoYmVhY2gpLiBUbyBicmluZyB0aGUgZGF0YSBiYWNrIHRvIHRoZSBFdmVudElEIGxldmVsLCB3ZSBuZWVkIHRvIGRlYWwgd2l0aCBzYW1wbGVzIHRoYXQgbm93IGhhdmUgbXVsdGlwbGUgU2l0ZUlEJ3MgYW5kIGxvY2F0aW9ucywgYXMgd2VsbCBhcyBwcmVzZXJ2ZSB0aGUgRXZlbnRJRCdzIGFzc29jaWF0ZWQgd2l0aCB0aGUgb3JpZ2luYWwgU2l0ZUlELiBUaGlzIG1lYW5zIHdlIHdpbGwgYmUgb3ZlcndyaXRpbmcgZGF0YSwgd2hpY2ggaXMgd2h5IEkgY2FsbGVkIHRoZSBhYm92ZSBzdWJzZXQgJ29yaWcnIGFuZCB0aGUgYmVsb3cgJ25ldycuCgpgYGB7cn0KbmV3ID0gb3JpZy5uZXN0ICU+JSAKICByb3d3aXNlKCkgJT4lICMgT3BlcmF0ZXMgYSByb3ctYXQtYS10aW1lIChwZXIgc2FtcGxlKQogIG11dGF0ZShTaXRlSUQuY2wgPSBtaW4oU2l0ZXMpLCAKICAgICAgICAgTGF0Lm1lYW4gPSBtZWFuKExhdHMkTGF0KSwKICAgICAgICAgTG9uLm1lYW4gPSBtZWFuKExvbnMkTG9uKSkgJT4lCiAgc2VsZWN0KC1jKFNpdGVzLCBMYXRzLCBMb25zKSkgJT4lICMgUmVtb3ZlIG9yaWdpbmFsIGluZm8gaW4gbmVzdGVkIHZhcmlhYmxlcwogIHVuaXRlKGNvbCA9IFZpc2l0SUQsIFNpdGVJRC5jbCwgRGF0ZSwgc2VwID0gIl8iLCByZW1vdmUgPSBGQUxTRSkgJT4lICMgY3JlYXRlIFZpc2l0SUQKICBzZWxlY3QoLWMoRGF0ZSwgU2l0ZUlELmNsLCBDbHVzdGVyKSkgJT4lICMgUmVtb3ZlIGluZm8gbm93IGNhcHR1cmVkIGJ5IFZpc2l0SUQKICB1bm5lc3RfbG9uZ2VyKEV2ZW50cykgJT4lICMgVW5uZXN0IHRvIHRoZSBFdmVudElEIGxldmVsCiAgdW5wYWNrKGNvbHMgPSBFdmVudHMpICMgY2hhbmdlIHRpYmJsZSBiYWNrIHRvIHZlY3RvcgoKIyBKb2luIG91ciBuZXcgaWRlbnRpZmllciB0byB0aGUgcmVzdCBvZiB0aGUgc2l0ZSBkYXRhLApldmVudHMgPSBsZWZ0X2pvaW4obmV3LCBldmVudHMuMywgYnkgPSAiRXZlbnRJRCIpICU+JQogIG11dGF0ZShMYXQgPSBMYXQubWVhbiwgTG9uID0gTG9uLm1lYW4pICU+JSAjIFJlcGxhY2Ugb2xkIGxhdC9sb24gd2l0aCBuZXcgbWVhbiBsYXQvbG9uCiAgc2VsZWN0KC1jKExhdC5tZWFuLCBMb24ubWVhbikpCgojIENoZWNrIG91ciBvdXIgbmV3IGRhdGEgc3RydWN0dXJlCmhlYWQoZXZlbnRzLCAxMikKYGBgCgpXZSBjYW4gc2VlIGluIGZpcnN0IGRvemVuIHJvd3MgaG93IHRoZSBsb2NhdGlvbnMgbm93IHN0cnVjdHVyZWQgYnkgVmlzaXRJRCBjb21wYXJlcyB0byB0aGUgb3JpZ2luYWwgYmFzZWQgb24gU2l0ZUlELiBUbyBzZWxlY3QgYW4gaWRlbnRpZmllciBmb3IgdGhlIG5ldyBWaXNpdElELCBJIHNpbXBseSB3ZW50IHdpdGggdGhlIGxvd2VzdCBTaXRlSUQgb2YgdGhlIG5lc3QgbGlzdCAodGhlcmUgd2VyZSBubyB3cm9uZyBhbnN3ZXJzIGhlcmUpLiBUaGUgbGF0L2xvbidzIHRvIGJlIGNvbWJpbmVkIGFyZSBhdCBtb3N0IGEgaGFuZGZ1bCBrbXMgYXBhcnQsIHNvIEkgZmlndXJlIGEgc2ltcGxlIG1lYW4gd291bGQgc3VmZmljZS4gTm90aWNlIHdlIGhhdmUgdW5pcXVlIGxhdC9sb24ncyBhdCB0aGUgVmlzaXRJRCBsZXZlbC0gdGhpcyBpcyBiZWNhdXNlIGRpZmZlcmVudCB2aXNpdHMgdG8gdGhlIHNhbWUgYmVhY2ggbWlnaHQgaGF2ZSBoYWQgYSBkaWZmZXJlbnQgbnVtYmVyIG9mIHJlcGxpY2F0ZXMuIEZvciBleGFtcGxlLCB3ZSBzZWUgdGhhdCBWaXNpdElEIDFfMjAwMS0wNy0yNCBpbnZvbHZlZCB0aGUgZmlyc3QgZml2ZSBldmVudHMgKEV2ZW50SUQncyAxNS0xOSksIHdoZXJlYXMsIFZpc2l0SUQgMV8yMDAyLTAzLTI1IGhhZCBhbiBhZGRpdGlvbmFsIHR3byBldmVudHMuCgpPSywgZ3JlYXQuIExhc3RseSwgbGV0J3MgcmV0dXJuIHRvIG91ciBmcmVxdWVuY3kgcGxvdHMgc2hvd2luZyB0aGUgbnVtYmVyIGV2ZW50cyBwZXIgc2FtcGxlOgoKYGBge3IgZWNobyA9IEZBTFNFfQojIFBsb3QgZXZlbnRzIHBlciBTaXRlSUQtRGF0ZSBzYW1lIGFzIGJlZm9yZSwKZXZlbnRzLjMgJT4lIHN1bW1hcmlzZShFdmVudHMgPSBuX2Rpc3RpbmN0KEV2ZW50SUQpLCAuYnkgPSBjKFNpdGVJRCwgRGF0ZSkpICU+JQogIGdncGxvdChkYXRhID0gLiwgYWVzKHggPSBFdmVudHMpKSArCiAgZ2VvbV9iYXIoYWVzKHkgPSBhZnRlcl9zdGF0KGNvdW50KSkpICsKICBnZW9tX3RleHQoc3RhdCA9ICdDb3VudCcsIGFlcyhsYWJlbCA9IGFmdGVyX3N0YXQoY291bnQpKSwgdmp1c3QgPSAtMC41KSArCiAgbGFicyh0aXRsZSA9ICJGcmVxdWVuY3kgb2YgZXZlbnRzIHBlciBzYW1wbGUgaW4gb3JpZ2luYWwgZGF0YSBzdHJ1Y3R1cmUiKQojIGdnc2F2ZSgibmZhX2ZyZXEtU2l0ZSZEYXRlLWJ5LXNlaW5lcy5wbmciLCBwbG90ID0gbGFzdF9wbG90KCksIGRldmljZSA9ICdwbmcnLCBwYXRoID0gZmlsZS5wYXRoKGRpci5maWdzKSkKCiMgQW5kIGV2ZW50cyBwZXIgdmlzaXQKZXZlbnRzICU+JSBzdW1tYXJpc2UoRXZlbnRzID0gbl9kaXN0aW5jdChFdmVudElEKSwgLmJ5ID0gVmlzaXRJRCkgJT4lCiAgZ2dwbG90KGRhdGEgPSAuLCBhZXMoeCA9IEV2ZW50cykpICsKICBnZW9tX2JhcihhZXMoeSA9IGFmdGVyX3N0YXQoY291bnQpKSkgKwogIGdlb21fdGV4dChzdGF0ID0gJ0NvdW50JywgYWVzKGxhYmVsID0gYWZ0ZXJfc3RhdChjb3VudCkpLCB2anVzdCA9IC0wLjUpICsKICBsYWJzKHRpdGxlID0gIkZyZXF1ZW5jeSBvZiBldmVudHMgcGVyIHNhbXBsZSBhZnRlciBzb2x2aW5nIFNpdGUtRXZlbnQgcHJvYmxlbSIpCiMgZ2dzYXZlKCJuZmFfZnJlcS12aXNpdHMtYnktc2VpbmVzLnBuZyIsIHBsb3QgPSBsYXN0X3Bsb3QoKSwgZGV2aWNlID0gJ3BuZycsIHBhdGggPSBmaWxlLnBhdGgoZGlyLmZpZ3MpKQoKYGBgCgpXb3cuIFRoZXJlIGFyZSB3YXkgbGVzcyBzaW5nbGUtcHVsbCBzYW1wbGVzISBBbHNvLCB0aGVyZSBhcmUgc29tZSBpbnRlcmVzdGluZyBzYW1wbGVzIHdpdGggcmVwbGljYXRlcyBvZiA5LCAxMCwgYW5kIDEyLiBTb21lIHN0YXRzIG9uIHRoZSBudW1iZXIgb2YgZXZlbnRzIHBlciB2aXNpdDoKCi0gICBtZWFuID0gYHIgc3VtbWFyaXNlKGV2ZW50cywgZXZlbnRzID0gbl9kaXN0aW5jdChFdmVudElEKSwgLmJ5ID0gVmlzaXRJRCkgJT4lIHNlbGVjdCgtVmlzaXRJRCkgJT4lIGFzX3ZlY3RvcigpICU+JSBtZWFuKCkgJT4lIHJvdW5kKDIpYAoKLSAgIG1lZGlhbiA9IGByIHN1bW1hcmlzZShldmVudHMsIGV2ZW50cyA9IG5fZGlzdGluY3QoRXZlbnRJRCksIC5ieSA9IFZpc2l0SUQpICU+JSBzZWxlY3QoLVZpc2l0SUQpICU+JSBhc192ZWN0b3IoKSAlPiUgbWVkaWFuKCkgJT4lIHJvdW5kKDIpYAoKLSAgIHNkID0gYHIgc3VtbWFyaXNlKGV2ZW50cywgZXZlbnRzID0gbl9kaXN0aW5jdChFdmVudElEKSwgLmJ5ID0gVmlzaXRJRCkgJT4lIHNlbGVjdCgtVmlzaXRJRCkgJT4lIGFzX3ZlY3RvcigpICU+JSBzZCgpICU+JSByb3VuZCgyKWAKCiMjIENsZWFuIHVwCgpgYGB7cn0KIyBDbGVhbiB1cCBvdXIgZW52aXJvbm1lbnQgZm9yIHRoZSBuZXh0IHN0ZXA6CnJtKGxpc3QgPSBscygpWyFscygpICVpbiUgYygnZXZlbnRzJywgJ2NhdGNoLjEnLCAnZGF0YScpXSkKCiMgUmVzZXQgZGlyZWN0b3J5CndkID0gaGVyZSgpCmRpcnMgPSB3ZCAlPiUgbGlzdC5maWxlcygpICU+JSBzdHJfc3Vic2V0KHBhdHRlcm4gPSAiXlJFQURNRXxeTElDRU5TRXwubWQkfC5ScHJvaiQiLCBuZWdhdGUgPSBUUlVFKQpmb3IgKGkgaW4gc2VxX2Fsb25nKGRpcnMpKSB7CiAgbmFtZSA9IHN0cl9yZXBsYWNlX2FsbChkaXJzW2ldLCAiXiIsICJkaXIuIikKICBwYXRoID0gc3RyX3JlcGxhY2VfYWxsKGRpcnNbaV0sICJeIiwgc3RyX2Mod2QsICIvIikpCiAgYXNzaWduKG5hbWUsIHBhdGgpCiAgcm0ocGF0aCwgaSkKfQpgYGAKCiMgV3JhbmdsaW5nIGNhdGNoIGRhdGEKClNpbWlsYXIgdG8gb3VyIHdyYW5nbGUgb2Ygc2l0ZSBpbmZvcm1hdGlvbiwgd2Ugd2FudCB0byBob25lIHRoZSBicmVhZHRoIG9mIHRoZSBjYXRjaCBkYXRhIHdlIHVzZS4gQXQgdGhpcyBzdGFnZSwgd2UgZG9uJ3Qgd2FudCB0byBtYWtlIGFueSAqaW50ZXJwcmV0YXRpb25zKiBhYm91dCB0aGUgY2F0Y2guIEluc3RlYWQsIHRoZSBnb2FsIGhlcmUgaXMgdG8gd2VlZCBvdXQgb2JqZWN0aXZlbHkgdW5uZWVkZWQgYW5kIHN1c3BpY2lvdXMgY2FzZXMgYW5kIGVuc3VyZSB0aGUgY29uc2lzdGVuY3kgb2YgdGhlIGRhdGEgKGUuZy4sIGFyZSBhbGwgcG9zc2libGUgc3BlY2llcyBuYW1lZCBhY3R1YWwgbmFtZXM/KS4KCiMjIFNraW1taW5nIGNhdGNoCgpgYGB7cn0Kc2tpbShjYXRjaC4xKQpgYGAKCk9uZSBuaWNlIHRoaW5nIGFib3V0ICoqc2tpbSgpKiogaXMgdGhhdCBpdCBxdWlja2x5IHRlbGxzIHlvdSB3aGVyZSB0aGUgaW5mb3JtYXRpb24gaXMgbWlzc2luZyBhbmQgaG93IG11Y2ggaXMgbWlzc2luZy4gV2Ugc2VlIHRoYXQgdGhlcmUgYXJlIG5pbmUgY2FzZXMgb2YgbWlzc2luZyBkYXRhIGF0IHRoZSBmYW1pbHkgbGV2ZWwuIFNvbWV0aGluZyBlbHNlIHRoYXQganVtcHMgb3V0IHRvIG1lIGlzIHRoYXQgdGhlIG51bWJlciBvZiB1bmlxdWUgc3BlY2llcyBjb21tb24gbmFtZXMgaXMgbm90IHRoZSBzYW1lIGFzIHRoZSBudW1iZXIgb2YgdW5pcXVlIHNwZWNpZXMgc2NpZW50aWZpYyBuYW1lcy4KCkknbGwgYWxzbyBjaGFuZ2UgdGhlICdVbm1lYXN1cmVkJyB2YXJpYWJsZSB0byBhIGNvdW50IGFidW5kYW5jZSwgd2hpY2ggbWF5IG5vdCBiZSB0b3RhbGx5IG5lY2Vzc2FyeSBidXQgd2lsbCBtYWtlIGl0IGVhc2llciBmb3IgbWUgdG8gd29yayB3aXRoIHRoZSBkYXRhLgoKQW5vdGhlciB0aGluZyB0byBwb2ludCBvdXQgaXMgdGhhdCBsZXNzIHRoYW4gaGFsZiBvZiB0aGUgZGF0YSBhcHBlYXJzIHRvIGhhdmUgbGlmZSBzdGFnZSBpbmZvcm1hdGlvbi4gRnVydGhlcm1vcmUsIHRoZSBkYXRhIHdpdGggbGVuZ3RoIGluZm9ybWF0aW9uIGlzIGEgbGl0dGxlIG92ZXIgNTAlLiBXZSBleHBlY3Qgc29tZSBtaXNzaW5nIGxlbmd0aHMgYmVjYXVzZSBtb3N0IHByb2plY3RzIHdpbGwgbWVhc3VyZSBzaXplIGZvciBhIHN1YnNldCBvZiBlYWNoIHNwZWNpZXMgYW5kIGNvdW50IHRoZSByZXN0LiBUeXBpY2FsbHksIEkgZXhwZWN0IDIwLTUwIGluZGl2aWR1YWxzIHNpemVkIGJlZm9yZSBhIHJvdyBpbmRpY2F0ZWQgdGhlIHVubWVhc3VyZWQgY291bnQsIHdoaWNoIHdvdWxkIHJlc3VsdCBpbiBhIHZlcnkgbG93IHBlcmNlbnRhZ2Ugb2YgIm1pc3NpbmciIGxlbmd0aHMgKG1heWJlIDUlIG9yIGxlc3M/KS4gQnV0IG1pc3NpbmcgbGVuZ3RocyBvdmVyIDUwJSBpbmRpY2F0ZSBzb21lIHByb2plY3RzIGp1c3QgZGlkbid0IG1lYXN1cmUgdGhlaXIgY2F0Y2gsIHdoaWNoIGlzIGEgc2hhbWUgYmVjYXVzZSB3ZSBjb3VsZCBoYXZlIGluZmVycmVkIGxpZmUgc3RhZ2UgZnJvbSBsZW5ndGguIEF0IHRoaXMgcG9pbnQsIGl0IGRvZXNuJ3Qgc2VlbSB3b3J0aCB3aGlsZSB0byB0b3NzIG91dCBwb3RlbnRpYWxseSBoYWxmIHRoZSBkYXRhIHRvIGluY2x1ZGUgZWl0aGVyIG9mIHRob3NlIHZhcmlhYmxlcy4gQWx0aG91Z2gsIEkgbWF5IHJldHVybiB0byB0aGlzIGRvd24gdGhlIHJvYWQuCgpGaXJzdCwgbGV0J3MgY2hlY2sgb3V0IGFsbCB0aGUgdGF4YSB3ZSBoYXZlIGluIHRoZSBkYXRhLgoKIyMgVGF4YSBsaXN0IC0gYmVmb3JlIGVkaXRzIHsudGFic2V0IC50YWJzZXQtcGlsbHN9CgojIyMgRmFtaWx5IHsudW5udW1iZXJlZH0KCmBgYHtyIGVjaG8gPSBGQUxTRX0KY2F0Y2guMSAlPiUKICBzZWxlY3QoRmFtX1NjaWVudGlmaWNOYW1lLCBGYW1fQ29tbW9uTmFtZSkgJT4lCiAgcmVuYW1lKGBTY2llbnRpZmljIE5hbWVgID0gRmFtX1NjaWVudGlmaWNOYW1lLCBgQ29tbW9uIE5hbWVgID0gRmFtX0NvbW1vbk5hbWUpICU+JQogIGRpc3RpbmN0KCkgJT4lCiAgYXJyYW5nZShgU2NpZW50aWZpYyBOYW1lYCkgJT4lCiAgZGF0YXRhYmxlKHJvd25hbWVzID0gRkFMU0UsCiAgICAgICAgICAgIGV4dGVuc2lvbnMgPSAnU2Nyb2xsZXInLAogICAgICAgICAgICBvcHRpb25zID0gbGlzdChkZWZlclJlbmRlciA9IFRSVUUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgIHNjcm9sbFkgPSA1MDAsCiAgICAgICAgICAgICAgICAgICAgICAgICAgIHNjcm9sbGVyID0gVFJVRSkpCmBgYAoKIyMjIFNwZWNpZXMgey51bm51bWJlcmVkfQoKYGBge3IgZWNobyA9IEZBTFNFfQpjYXRjaC4xICU+JQogIHNlbGVjdChTcF9TY2llbnRpZmljTmFtZSwgU3BfQ29tbW9uTmFtZSkgJT4lCiAgcmVuYW1lKGBTY2llbnRpZmljIE5hbWVgID0gU3BfU2NpZW50aWZpY05hbWUsIGBDb21tb24gTmFtZWAgPSBTcF9Db21tb25OYW1lKSAlPiUKICBkaXN0aW5jdCgpICU+JQogIGFycmFuZ2UoYFNjaWVudGlmaWMgTmFtZWApICU+JQogIGRhdGF0YWJsZShyb3duYW1lcyA9IEZBTFNFLAogICAgICAgICAgICBleHRlbnNpb25zID0gJ1Njcm9sbGVyJywKICAgICAgICAgICAgb3B0aW9ucyA9IGxpc3QoZGVmZXJSZW5kZXIgPSBUUlVFLAogICAgICAgICAgICAgICAgICAgICAgICAgICBzY3JvbGxZID0gNTAwLAogICAgICAgICAgICAgICAgICAgICAgICAgICBzY3JvbGxlciA9IFRSVUUpKQpgYGAKCiMjIEp1dmVuaWxlcyBhbmQgdW5pZGVudGlmaWVkIGZpc2ggey50YWJzZXQgLnRhYnNldC1waWxsc30KCiMjIyBKdXZlbmlsZXMgey51bm51bWJlcmVkfQpgYGB7ciBlY2hvID0gRkFMU0V9CiMgSnV2ZW5pbGUgc3BlY2llcyB0YWJsZQpjYXRjaC4xICU+JQogIGZpbHRlcihTcF9Db21tb25OYW1lICU+JSBzdHJfZGV0ZWN0KCJqdXZlbmlsZSIpKSAlPiUKICBzZWxlY3QoU3BfU2NpZW50aWZpY05hbWUsIFNwX0NvbW1vbk5hbWUsIFVubWVhc3VyZWQsIExlbmd0aF9tbSwgTGlmZVN0YWdlKSAlPiUKICByZW5hbWUoYFNjaWVudGlmaWMgTmFtZWAgPSBTcF9TY2llbnRpZmljTmFtZSwKICAgICAgICAgYENvbW1vbiBOYW1lYCA9IFNwX0NvbW1vbk5hbWUsCiAgICAgICAgIGBOIFVubWVhc3VyZWRgID0gVW5tZWFzdXJlZCwKICAgICAgICAgYExlbmd0aCAobW0pYCA9IExlbmd0aF9tbSwKICAgICAgICAgYExpZmUgU3RhZ2VgID0gTGlmZVN0YWdlKSAlPiUKICBhcnJhbmdlKGBTY2llbnRpZmljIE5hbWVgKSAlPiUKICBkYXRhdGFibGUocm93bmFtZXMgPSBGQUxTRSwKICAgICAgICAgICAgZXh0ZW5zaW9ucyA9ICdTY3JvbGxlcicsCiAgICAgICAgICAgIG9wdGlvbnMgPSBsaXN0KGRlZmVyUmVuZGVyID0gVFJVRSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgc2Nyb2xsWSA9IDUwMCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgc2Nyb2xsZXIgPSBUUlVFKSkKYGBgCgojIyMgVW5pZGVudGlmaWVkIHsudW5udW1iZXJlZH0KCmBgYCB7ciBlY2hvID0gRkFMU0V9CiMgVW5pZGVudGlmaWVkIHNwZWNpZXMgdGFibGUKY2F0Y2guMSAlPiUKICBmaWx0ZXIoU3BfQ29tbW9uTmFtZSAlPiUgc3RyX2RldGVjdCgidW5pZGVudGlmaWVkIikpICU+JQogIHNlbGVjdChTcF9TY2llbnRpZmljTmFtZSwgU3BfQ29tbW9uTmFtZSwgVW5tZWFzdXJlZCwgTGVuZ3RoX21tLCBMaWZlU3RhZ2UpICU+JQogIHJlbmFtZShgU2NpZW50aWZpYyBOYW1lYCA9IFNwX1NjaWVudGlmaWNOYW1lLAogICAgICAgICBgQ29tbW9uIE5hbWVgID0gU3BfQ29tbW9uTmFtZSwKICAgICAgICAgYE4gVW5tZWFzdXJlZGAgPSBVbm1lYXN1cmVkLAogICAgICAgICBgTGVuZ3RoIChtbSlgID0gTGVuZ3RoX21tLAogICAgICAgICBgTGlmZSBTdGFnZWAgPSBMaWZlU3RhZ2UpICU+JQogIGFycmFuZ2UoYFNjaWVudGlmaWMgTmFtZWApICU+JQogIGRhdGF0YWJsZShyb3duYW1lcyA9IEZBTFNFLAogICAgICAgICAgICBleHRlbnNpb25zID0gJ1Njcm9sbGVyJywKICAgICAgICAgICAgb3B0aW9ucyA9IGxpc3QoZGVmZXJSZW5kZXIgPSBUUlVFLAogICAgICAgICAgICAgICAgICAgICAgICAgICBzY3JvbGxZID0gNTAwLAogICAgICAgICAgICAgICAgICAgICAgICAgICBzY3JvbGxlciA9IFRSVUUpKQpgYGAKCiMjIyBCb3RoIHsudW5udW1iZXJlZH0KCmBgYHtyfQpjYXRjaC4xICU+JQogIGZpbHRlcihTcF9Db21tb25OYW1lICU+JSBzdHJfZGV0ZWN0KCJqdXZlbmlsZXx1bmlkZW50aWZpZWQiKSkgJT4lCiAgc2VsZWN0KEZhbV9TY2llbnRpZmljTmFtZSwgRmFtX0NvbW1vbk5hbWUsIFNwX1NjaWVudGlmaWNOYW1lLCBTcF9Db21tb25OYW1lKSAlPiUKICBkaXN0aW5jdCgpICU+JQogIGFycmFuZ2UoU3BfQ29tbW9uTmFtZSkgJT4lCiAgZGF0YXRhYmxlKHJvd25hbWVzID0gRkFMU0UsCiAgICAgICAgICAgIGV4dGVuc2lvbnMgPSAnU2Nyb2xsZXInLAogICAgICAgICAgICBvcHRpb25zID0gbGlzdChkZWZlclJlbmRlciA9IFRSVUUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgIHNjcm9sbFkgPSA1MDAsCiAgICAgICAgICAgICAgICAgICAgICAgICAgIHNjcm9sbGVyID0gVFJVRSkpCmBgYAojIyBGaXhpbmcgbmFtaW5nIGNvbnZlbnRpb25zCgpUaGVyZSBpcyBhIGZydXN0cmF0aW5nIG1peCBvZiBzbyBjYWxsZWQganV2ZW5pbGVzIHRoYXQgYXJlIGxhYmVsbGVkIFlPWSBvciBMYXJ2YWwgb3IgWU9ZLUxhcnZhbCB3aXRoIGluY29uc2lzdGVudCBiaW5uaW5nIG9mIGxlbmd0aHMuIEZvciBleGFtcGxlLCB3ZSBoYXZlIG9ic2VydmF0aW9ucyBvZiBqdXZlbmlsZSBzY3VscGluIGF0IDkgbW0gd2l0aCBib3RoIFlPWSBhbmQgbGFydmFsIGxhYmVscyBpbiB0aGUgZGF0YS4gVGhlIGxhcmdlc3QganV2ZW5pbGUgaXMgYSBncmVlbmxpbmcgYXQgNDYwIG1tLiBUaGUgbmV4dCBsYXJnZXN0IGp1dmVuaWxlIGlzIGEgc2N1bHBpbiBhdCAyOTggbW0uIENvbmZ1c2luZ2x5LCB0aGVyZSBhcmUgYWxzbyB0aHJlZSBjYXNlcyBvZiBqdXZlbmlsZSBzY3VscGluL3NhbG1vbmlkIHRoYXQgYXJlIGxhYmVsbGVkIHdpdGggYWR1bHQgbGlmZSBzdGFnZS4gQmVzaWRlcyB0aGF0LCB3aGVyZSBmaXNoIGFyZSB1bm1lYXN1cmVkIGFuZCBubyBsaWZlIHN0YWdlIGRlc2lnbmF0ZWQsIHdlIGNhbiBwcm9iYWJseSB0cnVzdCB0aGF0IHRoZXkgYXJlIGFjdHVhbGx5IGp1dmVuaWxlcy4KCldoZW4gd2UgbG9vayBhdCB0aGUgb2JzZXJ2YXRpb25zIHRoYXQgd2VyZSBsYWJlbGxlZCAndW5pZGVudGlmaWVkJywgd2UgZmluZCBzb21lIG1vcmUgaXNzdWVzIHRvIGFkZHJlc3MuIFVuaWRlbnRpZmllZCBjYXNlcyB1c3VhbGx5IGNvbWUgYWJvdXQgYmVjYXVzZSBmaXNoZXMgd2VyZSBlaXRoZXIgdG9vIHNtYWxsIHRvIGRpc3Rpbmd1aXNoIGluIHRoZSBmaWVsZCAoZS5nLiwgWU9ZIG1vcnBob2xvZ2llcyksIHdlcmUgdW5mYW1pbGlhciB0byB0aGUgc2FtcGxlciAoZS5nLiwgcmFyZSBzcGVjaWVzKSwgb3Igd2VyZSB0YXhhIHRoYXQgYXJlIGdlbmVyYWxseSBkaWZmaWN1bHQgdG8gZGlzdGluZ3Vpc2ggYmFzZWQgb24gbW9ycGhvbG9neSAoZS5nLiwgc2FsbW9uaWRzIG9yIGNpc2NvKS4gVGhlIHNtYWxsZXN0IHVuaWRlbnRpZmllZCBmaXNoIHdpdGggYW4gYWR1bHQgbGlmZSBzdGFnZSBsYWJlbCBpcyBhIDI3NSBtbSBjaXNjbyAiQ29yZWdvbnVzIHNwZWNpZXMiLiBUaGUgbGFyZ2VzdCB1bmlkZW50aWZpZWQgZmlzaCBsYWJlbGxlZCBsYXJ2YWwgaXMgYSA1MSBtbSAiT3NtZXJpZGFlIG9yIENsdXBlaWRhZSIuIFRoaXMgYnJpbmdzIHVwIGFuIGluIG5hbWluZyBjb252ZW50aW9uIG9mIHRoZSB1bmlkZW50aWZpZWQgZmlzaGVzLiBGb3IgZXhhbXBsZSwgd2UgaGF2ZSBhIGdlbmVyYWwgJ3VuaWRlbnRpZmllZCBmaXNoJyBidXQgYWxzbyAndW5pZGVudGlmaWVkIGZpc2ggbGFydmFlJyBhbmQgJ3VuaWRlbnRpZmllZCBsYXJ2YWwgZm9yYWdlIGZpc2gnLiBXaGljaCBicmluZ3MgdXAgYW5vdGhlciBpc3N1ZSB0aGF0IGZpc2ggYXJlIHVuaWRlbnRpZmllZCB0byBkaWZmZXJpbmcgdGF4b25vbWljIGxldmVscy0gc29tZXRpbWVzIGdlbnVzIG9yIGZhbWlseSBvciBzdWItZmFtaWx5LgoKSXQgZG9lc24ndCBtYWtlIHNlbnNlIHRvIG1lIHRvIGhhdmUgYSBqdXZlbmlsZSBkaXN0aW5jdGlvbiBhbW9uZyBzcGVjaWVzIG5hbWVzIG5vciBoYXZlIHNvIG1hbnkgdW5pcXVlIG5hbWVzIGFtb25nIHVuaWRlbnRpZmllZCBmaXNoLiBUaGUgc2ltcGxlc3QgZml4IGluIG15IG9waW5pb24gaXMganVzdCB0byBiZSBzdXJlIHRvIHVzZSBzY2llbnRpZmljIG5hbWVzIGluIGFuYWx5c2VzLiBXZSdsbCBoYXZlIHRvIGFkZHJlc3MgdGhlIHR3byBjYXNlcyB1c2luZyAib3IiIGluIFNwX1NjaWVudGlmaWNOYW1lIGZpcnN0LiBJIGFsc28gdGhpbmsgd2Ugc2hvdWxkIHRoZW4gcmVtb3ZlIGFsbCB1bmlkZW50aWZpZWQgaW5zdGFuY2VzIHRoYXQgYXJlIHNpbXBseSAiRGl2aXNpb24gVGVsZW9zdGVpIiBiZWNhdXNlIGl0IGlzIG5vdCBpbmZvcm1hdGl2ZSBmb3IgYmFzaWMgcXVlc3Rpb25zIG9mIHRheG9ub215LiBBZnRlciB0aGF0LCB3ZSBtYXkgd2lzaCB0byBhZ2dyZWdhdGUgY2VydGFpbiBncm91cHMgdG8gaGlnaGVyIHRheG9ub215IGJhc2VkIG9uIGhvdyBmcmVxdWVudCBvciByYXJlbHkgdGhleSBvY2N1ciBpbiB0aGUgZGF0YS4gU28gd2Ugc2hvdWxkIGNyZWF0ZSBhIGdlbnVzIGNsYXNzIHRvIHByZXNlcnZlIHRoYXQgaW5mb3JtYXRpb24gaW4gY2FzZSB3ZSBkZWNpZGUgdG8gbW92ZSB0byB0aGUgZmFtaWx5IGxldmVsIGxhdGVyLgoKSSBkbyB3YW50IHRvIHJldGFpbiB0aGUgJ0RpdmlzaW9uIFRlbGVvc3RlaSIgZGF0YSBhcyBhIHN1YnNldCBiZWNhdXNlIEkgdGhpbmsgaXQgY291bGQgYmUgdXNlZnVsIHdoZW4gYXNraW5nIGNlcnRhaW4gcXVlc3Rpb25zLiBJIGFsc28gdGhpbmsgd2UgY291bGQgcmV0dXJuIHRvIHRoZSBpZGVhIG9mIGNhdGVnb3JpemluZyBzcGVjaWVzIGJhc2VkIG9uIGxpZmUgc3RhZ2UgYnkgaW5mZXJyaW5nIG1pc3NpbmcgaW5mb3JtYXRpb24uIEl0IHdvdWxkIGJlIGludGVyZXN0aW5nIHRvIGV4YW1pbmUgd2hldGhlciB3ZSBjb3VsZCBwcmVkaWN0IHdoYXQgdGhvc2UgZmlzaGVzIG1pZ2h0IGJlIGJhc2VkIG9uIGNvLW9jY3VycmluZyBzcGVjaWVzLCB0aW1lIG9mIHllYXIsIGxvY2F0aW9uLCBldGMuCgpJIGRvIG5vdCBzaG93IHJlc3VsdHMgb2YgdGhlIGJlbG93IGNvZGUsIGJ1dCBJIHByb3ZpZGUgYW4gZWRpdGVkIHRheGEgbGlzdCBhbmQgc2VlIGFsc28gY29tbWVudHMgZm9yIHN0ZXBzIHRha2VuLgoKYGBge3IgcmVzdWx0cyA9ICJoaWRlIn0KIyBGaXggdGhlICJvciIgY2FzZXMgaW4gU3BfU2NpZW50aWZpY05hbWUKY2F0Y2guMWEgPSBjYXRjaC4xICU+JQogICMgRmluZCB0aGUgJ0FuaXNhcmNodXMgb3IgTHVtcGVudXMnIHNwZWNpZXMgYW5kIHJlcGxhY2Ugd2l0aCBGYW1fU2NpZW50aWZpY05hbWUKICBtdXRhdGUoU3BfU2NpZW50aWZpY05hbWUgPSBpZmVsc2Uoc3RyX2RldGVjdChTcF9TY2llbnRpZmljTmFtZSwgcGF0dGVybiA9ICJBbmlzYXJjaHVzIG1lZGl1cyBvciBMdW1wZW51cyBmYWJyaWNpaSIpLCBGYW1fU2NpZW50aWZpY05hbWUsIFNwX1NjaWVudGlmaWNOYW1lKSwKICAgICAgICAgIyBSZXBsYWNlIHRoZSBtaXNzaW5nIGZhbWlseSBpbmZvIGZvciAiT3NtZXJpZGFlIG9yIENsdXBpZGFlIiB3aXRoICJEaXZpc2lvbiBUZWxvZXN0ZWkiIGFuZCAiYm9ueSBmaXNoZXMiCiAgICAgICAgIEZhbV9Db21tb25OYW1lID0gaWZlbHNlKGlzLm5hKEZhbV9Db21tb25OYW1lKSwgImJvbnkgZmlzaGVzIiwgRmFtX0NvbW1vbk5hbWUpLAogICAgICAgICBGYW1fU2NpZW50aWZpY05hbWUgPSBpZmVsc2UoaXMubmEoRmFtX1NjaWVudGlmaWNOYW1lKSwgIkRpdmlzaW9uIFRlbGVvc3RlaSIsIEZhbV9TY2llbnRpZmljTmFtZSksCiAgICAgICAgICMgQ3JlYXRlIGdlbnVzIGNsYXNzIGJ5IHRha2luZyBmaXJzdCB3b3JkIG9mIFNwX1NjaWVudGlmaWNOYW1lCiAgICAgICAgIEdlbl9TY2llbnRpZmljTmFtZSA9IHdvcmQoU3BfU2NpZW50aWZpY05hbWUsIDEpKQoKIyBSZW1vdmUgYWxsIGluc3RhbmNlcyBvZiAiRGl2aXNpb24gVGVsZW9zdGVpIiBhbmQgc2F2ZSB0aGUgZGYKY2F0Y2guMiA9IGZpbHRlcihjYXRjaC4xYSwgRmFtX1NjaWVudGlmaWNOYW1lICE9ICJEaXZpc2lvbiBUZWxlb3N0ZWkiKQoKIyBTdG9yZSB0aGUgcmVtb3ZlZCBmaXNoZXMgYXMgLnJkcyBpbiBvdXIgZGF0YSBmb2xkZXIKZmlsdGVyKGNhdGNoLjFhLCBGYW1fU2NpZW50aWZpY05hbWUgPT0gIkRpdmlzaW9uIFRlbGVvc3RlaSIpICU+JQogIHNhdmVSRFMoLiwgZmlsZSA9IGZpbGUucGF0aChkaXIuZGF0YSwgIm5mYV90ZWxlb3N0ZWkucmRzIikpCmBgYAoKIyMgVGF4YSBsaXN0IC0gYWZ0ZXIgZWRpdHMgey50YWJzZXQgLnRhYnNldC1waWxsc30KCiMjIyBGYW1pbHkgey51bm51bWJlcmVkfQoKYGBge3IgZWNobyA9IEZBTFNFfQpjYXRjaC4yICU+JQogIHNlbGVjdChGYW1fU2NpZW50aWZpY05hbWUsIEZhbV9Db21tb25OYW1lKSAlPiUKICByZW5hbWUoYFNjaWVudGlmaWMgTmFtZWAgPSBGYW1fU2NpZW50aWZpY05hbWUsIGBDb21tb24gTmFtZWAgPSBGYW1fQ29tbW9uTmFtZSkgJT4lCiAgZGlzdGluY3QoKSAlPiUKICBhcnJhbmdlKGBTY2llbnRpZmljIE5hbWVgKSAlPiUKICBkYXRhdGFibGUocm93bmFtZXMgPSBGQUxTRSwKICAgICAgICAgICAgZXh0ZW5zaW9ucyA9ICdTY3JvbGxlcicsCiAgICAgICAgICAgIG9wdGlvbnMgPSBsaXN0KGRlZmVyUmVuZGVyID0gVFJVRSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgc2Nyb2xsWSA9IDUwMCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgc2Nyb2xsZXIgPSBUUlVFKSkKYGBgCgojIyMgR2VudXMgey51bm51bWJlcmVkfQoKYGBge3IgZWNobyA9IEZBTFNFfQpjYXRjaC4yICU+JQogIHNlbGVjdChgU2NpZW50aWZpYyBOYW1lYCA9IEdlbl9TY2llbnRpZmljTmFtZSkgJT4lCiAgZGlzdGluY3QoKSAlPiUKICBhcnJhbmdlKGBTY2llbnRpZmljIE5hbWVgKSAlPiUKICBkYXRhdGFibGUocm93bmFtZXMgPSBGQUxTRSwKICAgICAgICAgICAgZXh0ZW5zaW9ucyA9ICdTY3JvbGxlcicsCiAgICAgICAgICAgIG9wdGlvbnMgPSBsaXN0KGRlZmVyUmVuZGVyID0gVFJVRSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgc2Nyb2xsWSA9IDUwMCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgc2Nyb2xsZXIgPSBUUlVFKSkKYGBgCgojIyMgU3BlY2llcyB7LnVubnVtYmVyZWR9CgpgYGB7ciBlY2hvID0gRkFMU0V9CmNhdGNoLjIgJT4lCiAgc2VsZWN0KFNwX1NjaWVudGlmaWNOYW1lLCBTcF9Db21tb25OYW1lKSAlPiUKICByZW5hbWUoYFNjaWVudGlmaWMgTmFtZWAgPSBTcF9TY2llbnRpZmljTmFtZSwgYENvbW1vbiBOYW1lYCA9IFNwX0NvbW1vbk5hbWUpICU+JQogIGRpc3RpbmN0KCkgJT4lCiAgYXJyYW5nZShgU2NpZW50aWZpYyBOYW1lYCkgJT4lCiAgZGF0YXRhYmxlKHJvd25hbWVzID0gRkFMU0UsCiAgICAgICAgICAgIGV4dGVuc2lvbnMgPSAnU2Nyb2xsZXInLAogICAgICAgICAgICBvcHRpb25zID0gbGlzdChkZWZlclJlbmRlciA9IFRSVUUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgIHNjcm9sbFkgPSA1MDAsCiAgICAgICAgICAgICAgICAgICAgICAgICAgIHNjcm9sbGVyID0gVFJVRSkpCmBgYAoKIyMgUmVwbGFjaW5nICdVbm1lYXN1cmVkJyB3aXRoICdDb3VudCcKClRoZSBiZWxvdyBjb2RlIGNoYW5nZXMgdGhlICdVbm1lYXN1cmVkJyBwYXJhbWV0ZXIgaW50byAnQ291bnQnIGFidW5kYW5jZToKCmBgYHtyIHJlc3VsdHMgPSAiaGlkZSJ9CmNhdGNoLjMgPSBtdXRhdGUoY2F0Y2guMiwgQ291bnQgPSBpZmVsc2UoVW5tZWFzdXJlZCA9PSAwLCAxLCBVbm1lYXN1cmVkKSkgJT4lCiAgc2VsZWN0KC1Vbm1lYXN1cmVkKSAjIHJlbW92ZSB1bnVzZWQgcGFyYW1ldGVyCmBgYAoKTmV4dCwgSSdsbCBtYWtlIGdyYXBocyBhbmQgdGFibGVzIGRlc2NyaWJpbmcgdGhlIGFidW5kYW5jZSBhbmQgb2NjdXJyZW5jZSBvZiBvdXIgY2F0Y2g6CgojIyBWaXN1YWxpemluZyBjYXRjaCB7LnRhYnNldCAudGFic2V0LXBpbGxzfQoKIyMjIEZhbWlseSB7LnVubnVtYmVyZWR9CgpgYGB7ciBlY2hvID0gRkFMU0UsIGZpZy53aWR0aCA9IDEyLCBmaWcuaGVpZ2h0ID0gOH0KIyBGYW1pbHkgYWJ1bmRhbmNlCmZhbS5hYnVuID0gY2F0Y2guMyAlPiUKICBzdW1tYXJpc2UoQWJ1bmRhbmNlID0gc3VtKENvdW50KSwgLmJ5ID0gRmFtX1NjaWVudGlmaWNOYW1lKSAlPiUKICBtdXRhdGUoRmFtX1NjaWVudGlmaWNOYW1lID0gZmN0X3Jlb3JkZXIoYXMuZmFjdG9yKEZhbV9TY2llbnRpZmljTmFtZSksIGRlc2MoQWJ1bmRhbmNlKSkpCiMgUGxvdApnZ3Bsb3QoZGF0ID0gZmFtLmFidW4sIGFlcyh4ID0gRmFtX1NjaWVudGlmaWNOYW1lLCB5ID0gQWJ1bmRhbmNlKSkgKwogIGdlb21fY29sKCkgKwogIGdlb21fdGV4dChhZXMobGFiZWwgPSBBYnVuZGFuY2UpLCB2anVzdCA9IC0wLjUsIHNpemUgPSAyKSArCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA5MCwgaGp1c3QgPSAxKSkgKwogIGxhYnModGl0bGUgPSAnRmFtaWx5IGFidW5kYW5jZScsIHggPSAnRmFtaWx5JykKIyBnZ3NhdmUoIm5mYV9mYW1fYWJ1bl9yYXcucG5nIiwgcGxvdCA9IGxhc3RfcGxvdCgpLCBkZXZpY2UgPSAncG5nJywgcGF0aCA9IGZpbGUucGF0aChkaXIuZmlncykpCgojIEZhbWlseSBmcmVxdWVuY3kgb2Ygb2NjdXJyZW5jZQpmYW0uZnJlcS5vY2N1ciA9IGxlZnRfam9pbihjYXRjaC4zLCBzZWxlY3QoZXZlbnRzLCBWaXNpdElELCBFdmVudElEKSwgYnkgPSAiRXZlbnRJRCIpICU+JQogIHN1bW1hcmlzZShQcmVzZW5jZSA9IG5fZGlzdGluY3QoRmFtX1NjaWVudGlmaWNOYW1lKSwgLmJ5ID0gYyhWaXNpdElELCBGYW1fU2NpZW50aWZpY05hbWUpKSAlPiUKICBzdW1tYXJpc2UoT2NjdXJyZW5jZSA9IHN1bShQcmVzZW5jZSksIC5ieSA9IEZhbV9TY2llbnRpZmljTmFtZSkgJT4lCiAgbXV0YXRlKFBlcmNfT2NjdXJyZW5jZSA9IHJvdW5kKE9jY3VycmVuY2UgLyBuX2Rpc3RpbmN0KGV2ZW50cyRWaXNpdElEKSAqIDEwMCwgMiksCiAgICAgICAgIEZhbV9TY2llbnRpZmljTmFtZSA9IGZjdF9yZW9yZGVyKGFzLmZhY3RvcihGYW1fU2NpZW50aWZpY05hbWUpLCBkZXNjKFBlcmNfT2NjdXJyZW5jZSkpKQojIFBsb3QKZ2dwbG90KGRhdGEgPSBmYW0uZnJlcS5vY2N1ciwgYWVzKHggPSBGYW1fU2NpZW50aWZpY05hbWUsIHkgPSBQZXJjX09jY3VycmVuY2UpKSArCiAgZ2VvbV9jb2woKSArCiAgZ2VvbV90ZXh0KGFlcyhsYWJlbCA9IHJvdW5kKFBlcmNfT2NjdXJyZW5jZSwgMSkpLCBzaXplID0gMiwgdmp1c3QgPSAtMC41KSArCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA5MCwgaGp1c3QgPSAxKSkgKwogICAgbGFicyh0aXRsZSA9ICdGYW1pbHkgZnJlcXVlbmN5IG9mIG9jY3VycmVuY2UnLCB4ID0gJ0ZhbWlseScsIHkgPSAnUGVyY2VudCBvY2N1cnJlbmNlJykKIyBnZ3NhdmUoIm5mYV9mYW1fZnJlcS1vY2N1ci5wbmciLCBwbG90ID0gbGFzdF9wbG90KCksIGRldmljZSA9ICdwbmcnLCBwYXRoID0gZmlsZS5wYXRoKGRpci5maWdzKSkKCiMgVGFibGUgb2YgZmFtaWx5IGFidW5kYW5jZSBhbmQgb2NjdXJyZW5jZQpmdWxsX2pvaW4oZmFtLmFidW4sIGZhbS5mcmVxLm9jY3VyLCBieSA9ICdGYW1fU2NpZW50aWZpY05hbWUnKSAlPiUKICBzZWxlY3QoYFNjaWVudGlmaWMgTmFtZWAgPSBGYW1fU2NpZW50aWZpY05hbWUsIGBQZXJjZW50IE9jY3VycmVuY2VgID0gUGVyY19PY2N1cnJlbmNlLCBBYnVuZGFuY2UpICU+JQogIGFycmFuZ2UoYXMuY2hhcmFjdGVyKGBTY2llbnRpZmljIE5hbWVgKSkgJT4lCiAgZGF0YXRhYmxlKHJvd25hbWVzID0gRkFMU0UsCiAgICAgICAgICAgIGV4dGVuc2lvbnMgPSAnU2Nyb2xsZXInLAogICAgICAgICAgICBvcHRpb25zID0gbGlzdChkZWZlclJlbmRlciA9IFRSVUUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgIHNjcm9sbFkgPSA1MDAsCiAgICAgICAgICAgICAgICAgICAgICAgICAgIHNjcm9sbGVyID0gVFJVRSkpCmBgYAoKIyMjIEdlbnVzIHsudW5udW1iZXJlZH0KCmBgYHtyIGVjaG8gPSBGQUxTRSwgZmlnLndpZHRoID0gMTIsIGZpZy5oZWlnaHQgPSA4fQojIEdlbnVzIGFidW5kYW5jZQpnZW4uYWJ1biA9IGNhdGNoLjMgJT4lCiAgc3VtbWFyaXNlKEFidW5kYW5jZSA9IHN1bShDb3VudCksIC5ieSA9IEdlbl9TY2llbnRpZmljTmFtZSkgJT4lCiAgbXV0YXRlKEdlbl9TY2llbnRpZmljTmFtZSA9IGZjdF9yZW9yZGVyKGFzLmZhY3RvcihHZW5fU2NpZW50aWZpY05hbWUpLCBkZXNjKEFidW5kYW5jZSkpKQojIFBsb3QKZ2dwbG90KGRhdGEgPSBnZW4uYWJ1biwgYWVzKHggPSBHZW5fU2NpZW50aWZpY05hbWUsIHkgPSBBYnVuZGFuY2UpKSArCiAgZ2VvbV9jb2woKSArCiAgZ2VvbV90ZXh0KGFlcyhsYWJlbCA9IEFidW5kYW5jZSksIHZqdXN0ID0gLTAuNSwgc2l6ZSA9IDIpICsKICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwLCBoanVzdCA9IDEpKSArCiAgbGFicyh0aXRsZSA9ICdHZW51cyBhYnVuZGFuY2UnLCB4ID0gJ0dlbnVzJykKIyBnZ3NhdmUoIm5mYV9nZW5fYWJ1bl9yYXcucG5nIiwgcGxvdCA9IGxhc3RfcGxvdCgpLCBkZXZpY2UgPSAncG5nJywgcGF0aCA9IGZpbGUucGF0aChkaXIuZmlncykpCgojIEdlbnVzIGZyZXF1ZW5jeSBvZiBvY2N1cnJlbmNlCmdlbi5mcmVxLm9jY3VyID0gbGVmdF9qb2luKGNhdGNoLjMsIHNlbGVjdChldmVudHMsIFZpc2l0SUQsIEV2ZW50SUQpLCBieSA9ICJFdmVudElEIikgJT4lCiAgc3VtbWFyaXNlKFByZXNlbmNlID0gbl9kaXN0aW5jdChHZW5fU2NpZW50aWZpY05hbWUpLCAuYnkgPSBjKFZpc2l0SUQsIEdlbl9TY2llbnRpZmljTmFtZSkpICU+JQogIHN1bW1hcmlzZShPY2N1cnJlbmNlID0gc3VtKFByZXNlbmNlKSwgLmJ5ID0gR2VuX1NjaWVudGlmaWNOYW1lKSAlPiUKICBtdXRhdGUoUGVyY19PY2N1cnJlbmNlID0gcm91bmQoT2NjdXJyZW5jZSAvIG5fZGlzdGluY3QoZXZlbnRzJFZpc2l0SUQpICogMTAwLCAyKSwKICAgICAgICAgR2VuX1NjaWVudGlmaWNOYW1lID0gZmN0X3Jlb3JkZXIoYXMuZmFjdG9yKEdlbl9TY2llbnRpZmljTmFtZSksIGRlc2MoUGVyY19PY2N1cnJlbmNlKSkpCiMgUGxvdCAKZ2dwbG90KGRhdGEgPSBnZW4uZnJlcS5vY2N1ciwgYWVzKHggPSBHZW5fU2NpZW50aWZpY05hbWUsIHkgPSBQZXJjX09jY3VycmVuY2UpKSArCiAgZ2VvbV9jb2woKSArCiAgZ2VvbV90ZXh0KGFlcyhsYWJlbCA9IHJvdW5kKFBlcmNfT2NjdXJyZW5jZSwgMSkpLCBzaXplID0gMiwgdmp1c3QgPSAtMC41KSArCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA5MCwgaGp1c3QgPSAxKSkgKwogICAgbGFicyh0aXRsZSA9ICdHZW51cyBmcmVxdWVuY3kgb2Ygb2NjdXJyZW5jZScsIHggPSAnR2VudXMnLCB5ID0gJ1BlcmNlbnQgb2NjdXJyZW5jZScpCiMgZ2dzYXZlKCJuZmFfZ2VuX2ZyZXEtb2NjdXIucG5nIiwgcGxvdCA9IGxhc3RfcGxvdCgpLCBkZXZpY2UgPSAncG5nJywgcGF0aCA9IGZpbGUucGF0aChkaXIuZmlncykpCgojIFRhYmxlIG9mIGdlbnVzIGFidW5kYW5jZSBhbmQgb2NjdXJyZW5jZQpmdWxsX2pvaW4oZ2VuLmFidW4sIGdlbi5mcmVxLm9jY3VyLCBieSA9ICdHZW5fU2NpZW50aWZpY05hbWUnKSAlPiUKICBzZWxlY3QoYFNjaWVudGlmaWMgTmFtZWAgPSBHZW5fU2NpZW50aWZpY05hbWUsIGBQZXJjZW50IE9jY3VycmVuY2VgID0gUGVyY19PY2N1cnJlbmNlLCBBYnVuZGFuY2UpICU+JQogIGFycmFuZ2UoYXMuY2hhcmFjdGVyKGBTY2llbnRpZmljIE5hbWVgKSkgJT4lCiAgZGF0YXRhYmxlKHJvd25hbWVzID0gRkFMU0UsCiAgICAgICAgICAgIGV4dGVuc2lvbnMgPSAnU2Nyb2xsZXInLAogICAgICAgICAgICBvcHRpb25zID0gbGlzdChkZWZlclJlbmRlciA9IFRSVUUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgIHNjcm9sbFkgPSA1MDAsCiAgICAgICAgICAgICAgICAgICAgICAgICAgIHNjcm9sbGVyID0gVFJVRSkpCmBgYAoKIyMjIFNwZWNpZXMgey51bm51bWJlcmVkfQoKYGBge3IgZWNobyA9IEZBTFNFLCBmaWcud2lkdGggPSAxMiwgZmlnLmhlaWdodCA9IDh9CiMgU3BlY2llcyBhYnVuZGFuY2UKc3AuYWJ1biA9IGNhdGNoLjMgJT4lCiAgc3VtbWFyaXNlKEFidW5kYW5jZSA9IHN1bShDb3VudCksIC5ieSA9IFNwX1NjaWVudGlmaWNOYW1lKSAlPiUKICBtdXRhdGUoU3BfU2NpZW50aWZpY05hbWUgPSBmY3RfcmVvcmRlcihhcy5mYWN0b3IoU3BfU2NpZW50aWZpY05hbWUpLCBkZXNjKEFidW5kYW5jZSkpKQojIFBsb3QKZ2dwbG90KGRhdGEgPSBzcC5hYnVuLCBhZXMoeCA9IFNwX1NjaWVudGlmaWNOYW1lLCB5ID0gQWJ1bmRhbmNlKSkgKwogIGdlb21fY29sKCkgKwogIGdlb21fdGV4dChhZXMobGFiZWwgPSBBYnVuZGFuY2UpLCB2anVzdCA9IC0wLjUsIHNpemUgPSAyKSArCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA5MCwgaGp1c3QgPSAxKSkgKwogIGxhYnModGl0bGUgPSAnU3BlY2llcyBhYnVuZGFuY2UnLCB4ID0gJ1NwZWNpZXMnKQojIGdnc2F2ZSgibmZhX3NwX2FidW5fcmF3LnBuZyIsIHBsb3QgPSBsYXN0X3Bsb3QoKSwgZGV2aWNlID0gJ3BuZycsIHBhdGggPSBmaWxlLnBhdGgoZGlyLmZpZ3MpKQoKIyBTcGVjaWVzIGZyZXF1ZW5jeSBvZiBvY2N1cnJlbmNlCnNwLmZyZXEub2NjdXIgPSBsZWZ0X2pvaW4oY2F0Y2guMywgc2VsZWN0KGV2ZW50cywgVmlzaXRJRCwgRXZlbnRJRCksIGJ5ID0gIkV2ZW50SUQiKSAlPiUKICBzdW1tYXJpc2UoUHJlc2VuY2UgPSBuX2Rpc3RpbmN0KFNwX1NjaWVudGlmaWNOYW1lKSwgLmJ5ID0gYyhWaXNpdElELCBTcF9TY2llbnRpZmljTmFtZSkpICU+JQogIHN1bW1hcmlzZShPY2N1cnJlbmNlID0gc3VtKFByZXNlbmNlKSwgLmJ5ID0gU3BfU2NpZW50aWZpY05hbWUpICU+JQogIG11dGF0ZShQZXJjX09jY3VycmVuY2UgPSByb3VuZChPY2N1cnJlbmNlIC8gbl9kaXN0aW5jdChldmVudHMkVmlzaXRJRCkgKiAxMDAsIDIpLAogICAgICAgICBTcF9TY2llbnRpZmljTmFtZSA9IGZjdF9yZW9yZGVyKGFzLmZhY3RvcihTcF9TY2llbnRpZmljTmFtZSksIGRlc2MoUGVyY19PY2N1cnJlbmNlKSkpCiMgUGxvdApnZ3Bsb3QoZGF0YSA9IHNwLmZyZXEub2NjdXIsIGFlcyh4ID0gU3BfU2NpZW50aWZpY05hbWUsIHkgPSBQZXJjX09jY3VycmVuY2UpKSArCiAgZ2VvbV9jb2woKSArCiAgZ2VvbV90ZXh0KGFlcyhsYWJlbCA9IHJvdW5kKFBlcmNfT2NjdXJyZW5jZSwgMSkpLCBzaXplID0gMiwgdmp1c3QgPSAtMC41KSArCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA5MCwgaGp1c3QgPSAxKSkgKwogICAgbGFicyh0aXRsZSA9ICdTcGVjaWVzIGZyZXF1ZW5jeSBvZiBvY2N1cnJlbmNlJywgeCA9ICdTcGVjaWVzJywgeSA9ICdQZXJjZW50IG9jY3VycmVuY2UnKQojIGdnc2F2ZSgibmZhX3NwX2ZyZXEtb2NjdXIucG5nIiwgcGxvdCA9IGxhc3RfcGxvdCgpLCBkZXZpY2UgPSAncG5nJywgcGF0aCA9IGZpbGUucGF0aChkaXIuZmlncykpCgojIFRhYmxlIG9mIHNwZWNpZXMgYWJ1bmRhbmNlIGFuZCBvY2N1cnJlbmNlCmZ1bGxfam9pbihzcC5hYnVuLCBzcC5mcmVxLm9jY3VyLCBieSA9ICdTcF9TY2llbnRpZmljTmFtZScpICU+JQogIHNlbGVjdChgU2NpZW50aWZpYyBOYW1lYCA9IFNwX1NjaWVudGlmaWNOYW1lLCBgUGVyY2VudCBPY2N1cnJlbmNlYCA9IFBlcmNfT2NjdXJyZW5jZSwgQWJ1bmRhbmNlKSAlPiUKICBhcnJhbmdlKGFzLmNoYXJhY3RlcihgU2NpZW50aWZpYyBOYW1lYCkpICU+JQogIGRhdGF0YWJsZShyb3duYW1lcyA9IEZBTFNFLAogICAgICAgICAgICBleHRlbnNpb25zID0gJ1Njcm9sbGVyJywKICAgICAgICAgICAgb3B0aW9ucyA9IGxpc3QoZGVmZXJSZW5kZXIgPSBUUlVFLAogICAgICAgICAgICAgICAgICAgICAgICAgICBzY3JvbGxZID0gNTAwLAogICAgICAgICAgICAgICAgICAgICAgICAgICBzY3JvbGxlciA9IFRSVUUpKQpgYGAKCiMjIERlZmluaW5nIHJhcmUgdGF4YQoKUmVtb3ZpbmcgcmFyZSBzcGVjaWVzIGlzIGNvbW1vbiB3aGVuIGRlYWxpbmcgd2l0aCBjb21tdW5pdHkgZGF0YSBiZWNhdXNlIG9mIHRoZSBhZmZlY3QgdGhhdCB0aGUgbW9zdCByYXJlIHNwZWNpZXMgd291bGQgaGF2ZSBpbiB0eXBpY2FsIGFuYWx5c2VzIHRvIGJlIGNvbmR1Y3RlZCAoZS5nLiwgYW5hbHlzaXMgb2YgdmFyaWFuY2UpLiBEZXBlbmRpbmcgb24gb3VyIHJlc2VhcmNoIHF1ZXN0aW9ucywgd2UgbWF5IG5vdCBjb25zaWRlciByYXJlIGNhc2VzIGZ1bmN0aW9uYWwgbWVtYmVycyBvZiB0aGUgY29tbXVuaXR5LSBpbiB0aGVzZSBjYXNlcyBpdCBtYXkgbWFrZSBtb3JlIHNlbnNlIHRvIHZpZXcgdGhlIGNvbW11bml0eSBhcyB0aG9zZSB0YXhhIG1vc3Qgd2VsbCByZXByZXNlbnRlZC4gQnV0IGlmIEkgd2VyZSBtb3JlIGludGVyZXN0ZWQgaW4gcmljaG5lc3MsIHRoZW4gSSB3b3VsZCB3YW50IHRvIGNvbnNlcnZhdGl2ZWx5IHJldGFpbiByYXJlIHRheGEuCgpJIHRoaW5rIHdlIGNhbiBjb252aW5jZSBvdXJzZWx2ZXMgdGhhdCByZW1vdmluZyBzaW5nbGUgY291bnQgYW5kIGV2ZW4gc2luZ2xlIG9jY3VyZW5jZSBzcGVjaWVzIHdvdWxkIGJlIHJlYXNvbmFibGUsIGJlY2F1c2Ugd2UgZG8gbm90IGhhdmUgYW55IGluZm9ybWF0aW9uIHRvIGxldCB1cyBrbm93IHdoZXRoZXIgdGhvc2UgY2FzZXMgcmVwcmVzZW50IGFuIGFjdHVhbCBjb21tdW5pdHkgbWVtYmVyIG9yIHdoZXRoZXIgdGhleSByZXByZXNlbnQgc29tZSBzb3J0IG9mIHNhbXBsaW5nIGVycm9yIChlLmcuLCBtaXN0YWtlbmx5IGxhYmVsbGluZyBhbGwgdGhlIHNhbG1vbmlkcyBpbiBhIHBhcnRpY3VsYXIgc2FtcGxlIEF0bGFudGljIFNhbG1vbiByYXRoZXIgdGhhbiBhIFBhY2lmaWMpLiBPdXIgcmVzZWFyY2ggcXVlc3Rpb25zIGJyb2FkbHkgZGVhbCB3aXRoIHNwYXRpYWwgYW5kIHRlbXBvcmFsIGRpc3RyaWJ1dGlvbnMgb2YgY29tbXVuaXR5IHN0cnVjdHVyZSwgc28gSSB0aGluayBhICdtaWRkbGUgb2YgdGhlIHJvYWQnIGFwcHJvYWNoIGlzIGhvdyB3ZSdsbCB3YW50IHRvIHByb2NlZWQgKGF0IGxlYXN0IGZvciBub3cpLgoKV2UnbGwgd2FudCB0byBiZSBvYmplY3RpdmUgaW4gaG93IHdlIGRlZmluZSByYXJlIHRheGEuIFRvIG1lLCBpdCBtYWtlcyBzZW5zZSB0byBjb25zaWRlciBvY2N1cnJlbmNlIHJhdGhlciB0aGFuIGFidW5kYW5jZS4gT3VyIGRlY2lzaW9uIHNob3VsZCBiYWxhbmNlIHRoZSBpbmZvcm1hdGlvbiBsb3N0IHZzIHJldGFpbmVkLCBhbmQgY29uc2lkZXIgaG93IGl0IGFmZmVjdHMgZWFjaCBsZXZlbCBvZiB0YXhvbm9teS4gSG93ZXZlciwgSSBkbyBub3Qga25vdyB3aGF0IHRheGEgbGV2ZWwgbWFrZXMgbW9zdCBzZW5zZSBmb3IgZnV0dXJlIGFuYWx5c2VzIGF0IHRoaXMgcG9pbnQgc28gd2UnbGwgd2FudCB0byBrZWVwIG91ciBvcHRpb25zIG9wZW4uIEZvciBub3csIGxldCdzIGNvbnNpZGVyIGl0ICJyYXJlIiB3aGVuIHRheGEgYXBwZWFyIGluIGxlc3MgdGhhbiA1IHRvdGFsIHZpc2l0cyAobGVzcyB0aGFuIDAuMjUlIHBlcmNlbnQgb2NjdXJyZW5jZSkuIFdpdGggdGhhdCwgcmVtb3ZpbmcgcmFyZSB0YXhhIGNhdXNlczoKCi0gMTcuNiUgcmVkdWN0aW9uIGluIHJpY2huZXNzIGF0IHRoZSBGYW1pbHkgbGV2ZWwgKDYgb2YgMzQpCgotIDI1LjYlIHJlZHVjdGlvbiBpbiByaWNobmVzcyBhdCB0aGUgR2VudXMgbGV2ZWwgKDMwIG9mIDExNykKCi0gMzEuNCUgcmVkdWN0aW9uIGluIHJpY2huZXNzIGF0IHRoZSBTcGVjaWVzIExldmVsICg1OSBvZiAxODgpCgpOb3RlIHRoYXQgdGhpcyBmaXhlZCBjdXQtb2ZmIGFmZmVjdHMgc3BlY2llcyBkaXZlcnNpdHkgbW9yZSBzbyB0aGFuIGZhbWlseSBkaXZlcnNpdHkuIFNvbWUgb2YgdGhpcyBjYW4gYmUgZXhwbGFpbmVkIGJ5IHRoZSBmYWN0IHRoYXQgdGhlIHNwZWNpZXMgbGlzdCBjb250YWlucyBtYW55IGlkZW50aWZpY2F0aW9ucyBhdCB0aGUgZ2VudXMgYW5kIGZhbWlseSBsZXZlbCwgd2hpY2ggbWFrZXMgc2Vuc2Ugc2luY2UgaWRlbnRpZmljYXRpb24gb2Z0ZW4gaW5jbHVkZXMgY2FzZXMgd2hlcmUgcmVzZWFyY2hlcnMgYWdncmVnYXRlICdpZmZ5JyBJRCdzIGludG8gYSBoaWdoZXIgY2xhc3MuIEkgZXhwZWN0IHRoaXMgaXNzdWUgdG8gYmUgZXNwZWNpYWxseSBhcHBhcmVudCBpbiBhIGNvbGxhYm9yYXRpdmUgZGF0YXNldCBsaWtlIHRoZSBORkEuCgojIyBMaXN0cyBvZiByYXJlIHRheGEgey50YWJzZXQgLnRhYnNldC1waWxsc30KCiMjIyBGYW1pbHkgey51bm51bWJlcmVkfQoKYGBge3IgZWNobyA9IEZBTFNFfQojIFN1YnNldCByYXJlIGZhbWlsaWVzCnJhcmUuZmFtID0gZnVsbF9qb2luKGZhbS5hYnVuLCBmYW0uZnJlcS5vY2N1ciwgYnkgPSAnRmFtX1NjaWVudGlmaWNOYW1lJykgJT4lCiAgZmlsdGVyKFBlcmNfT2NjdXJyZW5jZSA8IDAuMjUpICU+JQogIHNlbGVjdChGYW1fU2NpZW50aWZpY05hbWUsIFBlcmNfT2NjdXJyZW5jZSwgQWJ1bmRhbmNlKQojIFRhYmxlIG9mIHJhcmUgZmFtaWx5IGFidW5kYW5jZSBhbmQgb2NjdXJyZW5jZQpyZW5hbWUocmFyZS5mYW0sIGBTY2llbnRpZmljIE5hbWVgID0gRmFtX1NjaWVudGlmaWNOYW1lLCBgUGVyY2VudCBPY2N1cnJlbmNlYCA9IFBlcmNfT2NjdXJyZW5jZSkgJT4lCiAgYXJyYW5nZShhcy5jaGFyYWN0ZXIoYFNjaWVudGlmaWMgTmFtZWApKSAlPiUKICBkYXRhdGFibGUocm93bmFtZXMgPSBGQUxTRSwKICAgICAgICAgICAgZXh0ZW5zaW9ucyA9ICdTY3JvbGxlcicsCiAgICAgICAgICAgIG9wdGlvbnMgPSBsaXN0KGRlZmVyUmVuZGVyID0gVFJVRSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgc2Nyb2xsWSA9IDI1MCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgc2Nyb2xsZXIgPSBUUlVFKSkKYGBgCgojIyMgR2VudXMgey51bm51bWJlcmVkfQoKYGBge3IgZWNobyA9IEZBTFNFfQojIFN1YnNldCByYXJlIGdlbnVzCnJhcmUuZ2VuID0gZnVsbF9qb2luKGdlbi5hYnVuLCBnZW4uZnJlcS5vY2N1ciwgYnkgPSAnR2VuX1NjaWVudGlmaWNOYW1lJykgJT4lCiAgZmlsdGVyKFBlcmNfT2NjdXJyZW5jZSA8IDAuMjUpICU+JQogIHNlbGVjdChHZW5fU2NpZW50aWZpY05hbWUsIFBlcmNfT2NjdXJyZW5jZSwgQWJ1bmRhbmNlKQojIFRhYmxlIG9mIHJhcmUgZ2VudXMgYWJ1bmRhbmNlIGFuZCBvY2N1cnJlbmNlCnJlbmFtZShyYXJlLmdlbiwgYFNjaWVudGlmaWMgTmFtZWAgPSBHZW5fU2NpZW50aWZpY05hbWUsIGBQZXJjZW50IE9jY3VycmVuY2VgID0gUGVyY19PY2N1cnJlbmNlKSAlPiUKICBhcnJhbmdlKGFzLmNoYXJhY3RlcihgU2NpZW50aWZpYyBOYW1lYCkpICU+JQogIGRhdGF0YWJsZShyb3duYW1lcyA9IEZBTFNFLAogICAgICAgICAgICBleHRlbnNpb25zID0gJ1Njcm9sbGVyJywKICAgICAgICAgICAgb3B0aW9ucyA9IGxpc3QoZGVmZXJSZW5kZXIgPSBUUlVFLAogICAgICAgICAgICAgICAgICAgICAgICAgICBzY3JvbGxZID0gMjUwLAogICAgICAgICAgICAgICAgICAgICAgICAgICBzY3JvbGxlciA9IFRSVUUpKQpgYGAKCiMjIyBTcGVjaWVzIHsudW5udW1iZXJlZH0KCmBgYHtyIGVjaG8gPSBGQUxTRX0KIyBTdWJzZXQgcmFyZSBzcGVjaWVzCnJhcmUuc3AgPSBmdWxsX2pvaW4oc3AuYWJ1biwgc3AuZnJlcS5vY2N1ciwgYnkgPSAnU3BfU2NpZW50aWZpY05hbWUnKSAlPiUKICBmaWx0ZXIoUGVyY19PY2N1cnJlbmNlIDwgMC4yNSkgJT4lCiAgc2VsZWN0KFNwX1NjaWVudGlmaWNOYW1lLCBQZXJjX09jY3VycmVuY2UsIEFidW5kYW5jZSkKIyBUYWJsZSBvZiByYXJlIGdlbnVzIGFidW5kYW5jZSBhbmQgb2NjdXJyZW5jZQpyZW5hbWUocmFyZS5zcCwgYFNjaWVudGlmaWMgTmFtZWAgPSBTcF9TY2llbnRpZmljTmFtZSwgYFBlcmNlbnQgT2NjdXJyZW5jZWAgPSBQZXJjX09jY3VycmVuY2UpICU+JQogIGFycmFuZ2UoYXMuY2hhcmFjdGVyKGBTY2llbnRpZmljIE5hbWVgKSkgJT4lCiAgZGF0YXRhYmxlKHJvd25hbWVzID0gRkFMU0UsCiAgICAgICAgICAgIGV4dGVuc2lvbnMgPSAnU2Nyb2xsZXInLAogICAgICAgICAgICBvcHRpb25zID0gbGlzdChkZWZlclJlbmRlciA9IFRSVUUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgIHNjcm9sbFkgPSAyNTAsCiAgICAgICAgICAgICAgICAgICAgICAgICAgIHNjcm9sbGVyID0gVFJVRSkpCmBgYAoKQ29vbC0gbm93IGxldCdzIHJlLXZpc2l0IG91ciBhYnVuZGFuY2UgYW5kIG9jY3VycmVuY2UgZ3JhcGhzIHRvIHNlZSBob3cgdGhleSBsb29rOgoKIyMgQ2F0Y2ggYWZ0ZXIgcmVtb3ZpbmcgcmFyZSB0YXhhIHsudGFic2V0IC50YWJzZXQtcGlsbHN9CgojIyMgRmFtaWx5IHsudW5udW1iZXJlZH0KCmBgYHtyIGVjaG8gPSBGQUxTRSwgZmlnLndpZHRoID0gMTIsIGZpZy5oZWlnaHQgPSA4fQojIFBsb3QgYWJ1bmRhbmNlCmFudGlfam9pbihmYW0uYWJ1biwgcmFyZS5mYW0sIGJ5ID0gIkZhbV9TY2llbnRpZmljTmFtZSIpICU+JQogIGdncGxvdChhZXMoeCA9IEZhbV9TY2llbnRpZmljTmFtZSwgeSA9IEFidW5kYW5jZSkpICsKICBnZW9tX2NvbCgpICsKICBnZW9tX3RleHQoYWVzKGxhYmVsID0gQWJ1bmRhbmNlKSwgdmp1c3QgPSAtMC41LCBzaXplID0gMikgKwogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gOTAsIGhqdXN0ID0gMSkpICsKICBsYWJzKHRpdGxlID0gJ0ZhbWlseSBhYnVuZGFuY2UgZXhjbHVkaW5nIHJhcmUgdGF4YScsIHggPSAnRmFtaWx5JykKIyBnZ3NhdmUoIm5mYV9mYW1fYWJ1bl9yYXdfZXgtcmFyZS5wbmciLCBwbG90ID0gbGFzdF9wbG90KCksIGRldmljZSA9ICdwbmcnLCBwYXRoID0gZmlsZS5wYXRoKGRpci5maWdzKSkKCiMgUGxvdCBmcmVxdWVuY3kgb2Ygb2NjdXJyZW5jZQphbnRpX2pvaW4oZmFtLmZyZXEub2NjdXIsIHJhcmUuZmFtLCBieSA9ICJGYW1fU2NpZW50aWZpY05hbWUiKSAlPiUKICBnZ3Bsb3QoYWVzKHggPSBGYW1fU2NpZW50aWZpY05hbWUsIHkgPSBQZXJjX09jY3VycmVuY2UpKSArCiAgZ2VvbV9jb2woKSArCiAgZ2VvbV90ZXh0KGFlcyhsYWJlbCA9IHJvdW5kKFBlcmNfT2NjdXJyZW5jZSwgMSkpLCBzaXplID0gMiwgdmp1c3QgPSAtMC41KSArCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA5MCwgaGp1c3QgPSAxKSkgKwogICAgbGFicyh0aXRsZSA9ICdGYW1pbHkgZnJlcXVlbmN5IG9mIG9jY3VycmVuY2UgZXhjbHVkaW5nIHJhcmUgdGF4YScsIHggPSAnRmFtaWx5JywgeSA9ICdQZXJjZW50IG9jY3VycmVuY2UnKQojIGdnc2F2ZSgibmZhX2ZhbV9mcmVxLW9jY3VyX2V4LXJhcmUucG5nIiwgcGxvdCA9IGxhc3RfcGxvdCgpLCBkZXZpY2UgPSAncG5nJywgcGF0aCA9IGZpbGUucGF0aChkaXIuZmlncykpCmBgYAoKIyMjIEdlbnVzIHsudW5udW1iZXJlZH0KCmBgYHtyIGVjaG8gPSBGQUxTRSwgZmlnLndpZHRoID0gMTIsIGZpZy5oZWlnaHQgPSA4fQojIFBsb3QgYWJ1bmRhbmNlCmFudGlfam9pbihnZW4uYWJ1biwgcmFyZS5nZW4pICU+JQogIGdncGxvdChhZXMoeCA9IEdlbl9TY2llbnRpZmljTmFtZSwgeSA9IEFidW5kYW5jZSkpICsKICBnZW9tX2NvbCgpICsKICBnZW9tX3RleHQoYWVzKGxhYmVsID0gQWJ1bmRhbmNlKSwgdmp1c3QgPSAtMC41LCBzaXplID0gMikgKwogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gOTAsIGhqdXN0ID0gMSkpICsKICBsYWJzKHRpdGxlID0gJ0dlbnVzIGFidW5kYW5jZSBleGNsdWRpbmcgcmFyZSB0YXhhJywgeCA9ICdHZW51cycpCiMgZ2dzYXZlKCJuZmFfZ2VuX2FidW5fcmF3X2V4LXJhcmUucG5nIiwgcGxvdCA9IGxhc3RfcGxvdCgpLCBkZXZpY2UgPSAncG5nJywgcGF0aCA9IGZpbGUucGF0aChkaXIuZmlncykpCgojIFBsb3QgZnJlcXVlbmN5IG9mIG9jY3VycmVuY2UKYW50aV9qb2luKGdlbi5mcmVxLm9jY3VyLCByYXJlLmdlbikgJT4lCiAgZ2dwbG90KGFlcyh4ID0gR2VuX1NjaWVudGlmaWNOYW1lLCB5ID0gUGVyY19PY2N1cnJlbmNlKSkgKwogIGdlb21fY29sKCkgKwogIGdlb21fdGV4dChhZXMobGFiZWwgPSByb3VuZChQZXJjX09jY3VycmVuY2UsIDEpKSwgc2l6ZSA9IDIsIHZqdXN0ID0gLTAuNSkgKwogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gOTAsIGhqdXN0ID0gMSkpICsKICAgIGxhYnModGl0bGUgPSAnR2VudXMgZnJlcXVlbmN5IG9mIG9jY3VycmVuY2UgZXhjbHVkaW5nIHJhcmUgdGF4YScsIHggPSAnR2VudXMnLCB5ID0gJ1BlcmNlbnQgb2NjdXJyZW5jZScpCiMgZ2dzYXZlKCJuZmFfZ2VuX2ZyZXEtb2NjdXJfZXgtcmFyZS5wbmciLCBwbG90ID0gbGFzdF9wbG90KCksIGRldmljZSA9ICdwbmcnLCBwYXRoID0gZmlsZS5wYXRoKGRpci5maWdzKSkKYGBgCgojIyMgU3BlY2llcyB7LnVubnVtYmVyZWR9CgpgYGB7ciBlY2hvID0gRkFMU0UsIGZpZy53aWR0aCA9IDEyLCBmaWcuaGVpZ2h0ID0gOH0KIyBQbG90IGFidW5kYW5jZQphbnRpX2pvaW4oc3AuYWJ1biwgcmFyZS5zcCkgJT4lCiAgZ2dwbG90KGFlcyh4ID0gU3BfU2NpZW50aWZpY05hbWUsIHkgPSBBYnVuZGFuY2UpKSArCiAgZ2VvbV9jb2woKSArCiAgZ2VvbV90ZXh0KGFlcyhsYWJlbCA9IEFidW5kYW5jZSksIHZqdXN0ID0gLTAuNSwgc2l6ZSA9IDIpICsKICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwLCBoanVzdCA9IDEpKSArCiAgbGFicyh0aXRsZSA9ICdTcGVjaWVzIGFidW5kYW5jZSBleGNsdWRpbmcgcmFyZSB0YXhhJywgeCA9ICdTcGVjaWVzJykKIyBnZ3NhdmUoIm5mYV9zcF9hYnVuX3Jhd19leC1yYXJlLnBuZyIsIHBsb3QgPSBsYXN0X3Bsb3QoKSwgZGV2aWNlID0gJ3BuZycsIHBhdGggPSBmaWxlLnBhdGgoZGlyLmZpZ3MpKQoKIyBQbG90IGZyZXF1ZW5jeSBvZiBvY2N1cnJlbmNlCmFudGlfam9pbihzcC5mcmVxLm9jY3VyLCByYXJlLnNwKSAlPiUKICBnZ3Bsb3QoYWVzKHggPSBTcF9TY2llbnRpZmljTmFtZSwgeSA9IFBlcmNfT2NjdXJyZW5jZSkpICsKICBnZW9tX2NvbCgpICsKICBnZW9tX3RleHQoYWVzKGxhYmVsID0gcm91bmQoUGVyY19PY2N1cnJlbmNlLCAxKSksIHNpemUgPSAyLCB2anVzdCA9IC0wLjUpICsKICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwLCBoanVzdCA9IDEpKSArCiAgICBsYWJzKHRpdGxlID0gJ1NwZWNpZXMgZnJlcXVlbmN5IG9mIG9jY3VycmVuY2UgZXhjbHVkaW5nIHJhcmUgdGF4YScsIHggPSAnU3BlY2llcycsIHkgPSAnUGVyY2VudCBvY2N1cnJlbmNlJykKIyBnZ3NhdmUoIm5mYV9zcF9mcmVxLW9jY3VyX2V4LXJhcmUucG5nIiwgcGxvdCA9IGxhc3RfcGxvdCgpLCBkZXZpY2UgPSAncG5nJywgcGF0aCA9IGZpbGUucGF0aChkaXIuZmlncykpCmBgYAoKTGFzdCB0aGluZyB3ZSdsbCBkbyBpcyByZW5hbWUgb3VyIGNhdGNoIHRpYmJsZSBhbmQgY2xlYW4gdXAgb3VyIGVudmlyb25tZW50LgoKYGBge3J9CiMgUmUtYXNzaWduIHdvcmtpbmcgdmVyc2lvbiBvZiBjYXRjaCB0byBhIGNsZWFuZXIgbmFtZQpjYXRjaCA9IGNhdGNoLjMKYGBgCgojIyBDbGVhbiB1cAoKYGBge3J9CiMgQ2xlYW4gdXAgb3VyIGVudmlyb25tZW50IGZvciB0aGUgbmV4dCBzdGVwOgpybShsaXN0ID0gbHMoKVshbHMoKSAlaW4lIGMoJ2RhdGEnLCAnZXZlbnRzJywgJ2NhdGNoJywgJ2NhdGNoLmZhbScsICdjYXRjaC5nZW4nLCAnY2F0Y2guc3AnLCAncmFyZS5mYW1pbGllcycsICdyYXJlLmdlbnVzJywgJ3JhcmUuc3BlY2llcycpXSkKCiMgUmVzZXQgZGlyZWN0b3J5CndkID0gaGVyZSgpCmRpcnMgPSB3ZCAlPiUgbGlzdC5maWxlcygpICU+JSBzdHJfc3Vic2V0KHBhdHRlcm4gPSAiXlJFQURNRXxeTElDRU5TRXwubWQkfC5ScHJvaiQiLCBuZWdhdGUgPSBUUlVFKQpmb3IgKGkgaW4gc2VxX2Fsb25nKGRpcnMpKSB7CiAgbmFtZSA9IHN0cl9yZXBsYWNlX2FsbChkaXJzW2ldLCAiXiIsICJkaXIuIikKICBwYXRoID0gc3RyX3JlcGxhY2VfYWxsKGRpcnNbaV0sICJeIiwgc3RyX2Mod2QsICIvIikpCiAgYXNzaWduKG5hbWUsIHBhdGgpCiAgcm0ocGF0aCwgaSkKfQpgYGAKCiMgV3JhbmdsaW5nIHZpc2l0IGRhdGEKCiMjIFNldCB1cAoKUmVtZW1iZXIgdGhhdCBvdXIgc2FtcGxlcyBvcGVyYXRlIGF0IHRoZSB2aXNpdCBsZXZlbCwgd2hlcmUgdW5pcXVlIGV2ZW50cyByZXByZXNlbnQgcmVwbGljYXRlcyB3aXRoaW4gZWFjaCBzYW1wbGUuIExldCdzIGNyZWF0ZSBhIG5ldyB0aWJibGUgd2hlcmUgZWFjaCByb3cgcmVwcmVzZW50cyBhIHNhbXBsZS4gV2UnbGwgd2FudCB0byBrZWVwIHRyYWNrIG9mIGhvdyBtYW55IHJlcGxpY2F0ZXMgZWFjaCBzYW1wbGUgY29udGFpbnMgc28gdGhhdCB3ZSBtYXkgYWNjb3VudCBmb3IgdGhpcyBpbiBmdXR1cmUgYW5hbHlzZXMuCgpgYGB7cn0KIyBOZXcgdGliYmxlIGZvciB2aXNpdCBsZXZlbCB3cmFuZ2xpbmcsCnZpc2l0cy4xID0gc2VsZWN0KGV2ZW50cywgVmlzaXRJRCwgRXZlbnRJRCkgJT4lCiAgc3VtbWFyaXNlKFJlcGxpY2F0ZXMgPSBuX2Rpc3RpbmN0KEV2ZW50SUQpLCAuYnkgPSBWaXNpdElEKSAlPiUKICByaWdodF9qb2luKHNlbGVjdChldmVudHMsIC1TaXRlSUQsIC1FdmVudElEKSwgYnkgPSAiVmlzaXRJRCIpICU+JQogIGRpc3RpbmN0KCkKdmlzaXRzLjEgJT4lIG5yb3coKQpgYGAKCiMjIFNraW1taW5nIHZpc2l0cwoKVGhlcmUgYXJlIHN1cHBvc2VkIHRvIGJlIGByIHZpc2l0cy4xJFZpc2l0SUQgJT4lIG5fZGlzdGluY3QoKWAgdmlzaXRzLCBidXQgd2Ugc2VlIG1vcmUgcm93cyB0aGFuIHRoYXQgaW4gb3VyIGRhdGFzZXQuIFdoZW4gd2UgbWVyZ2VkIHNpdGVzIHRvZ2V0aGVyLCB0aGVyZSBtdXN0J3ZlIGJlZW4gc2FtcGxlcyB0aGF0IGNvbnRhaW5lZCBtdXRsaXBsZSBlbnRyaWVzIGZvciBzb21lIG9mIG91ciB2YXJpYWJsZXMgKGUuZy4sIG1vcmUgdGhhbiBvbmUgTG9jYXRpb24gb3IgSGFiaXRhdCkuIExldCdzIHNraW0gdGhlIGRhdGEgdG8gcmVtaW5kIG91cnNlbHZlcyBvZiBvdXIgdmFyaWFibGVzOgoKYGBge3J9CnNraW0odmlzaXRzLjEpCmBgYAoKUmlnaHQgYXdheSwgd2UgY2FuIHJ1bGUgb3V0IHRoZSB2YXJpYWJsZXMgdGhhdCB3ZSd2ZSBhbHJlYWR5IGRlYWx0IHdpdGggYW5kIGtub3cgYXJlIHVuaXF1ZSBhdCB0aGUgdmlzaXQgbGV2ZWw6IFZpc2l0SUQsIExhdC9Mb24sIERhdGUsIGFuZCBDbHVzdGVyLgoKV2UgY2FuIGFsc28gc2ltcGx5IGRyb3AgdGhlIGluZm9ybWF0aW9uIHRoYXQgd2UgZG9uJ3QgbmVlZCB0byBrZWVwIGluIHRoaXMgdmVyc2lvbiBvZiB0aGUgZGF0YSwgbmFtZWx5IFByb2plY3ROYW1lLCBQb2ludE9mQ29udGFjdCwgYW5kIEdlYXIgU3BlY2lmaWMuIEkgdGhpbmsgd2UgY2FuIGFsc28gcmVtb3ZlIHRoZSBUaWRhbFN0YWdlIHBhcmFtZXRlciwgYmVjYXVzZSBJIGRvbid0IGNhcmUgdG8gYWNjb3VudCBmb3IgaXQgYXQgdGhpcyBwb2ludC4KCmBgYHtyfQojIFVwZGF0ZSB0aWJibGUgdG8gcmVtb3ZlIHVubmVjZXNzYXJ5IHNpdGUgaW5mbywKdmlzaXRzLjIgPSBzZWxlY3QodmlzaXRzLjEsIC1jKFByb2plY3ROYW1lLCBQb2ludE9mQ29udGFjdCwgR2VhclNwZWNpZmljLCBUaWRhbFN0YWdlKSkgJT4lIGRpc3RpbmN0KCkKdmlzaXRzLjIgJT4lIG5yb3coKQpgYGAKClN0aWxsIG5vdCBxdWl0ZSB0aGVyZS4gSSBiZXQgYSBsb3Qgb2YgdmlzaXRzIGNvbnRhaW4gbXVsdGlwbGUgdGVtcHMsIHNhbGluaXR5LCBoYWJpdGF0cywgYW5kIHByb2JhYmx5IGxvY2F0aW9ucy4gV2UgbWlnaHQgd2FudCB0byB1c2UgdGhvc2UgbGF0ZXIgb24sIGJ1dCBmb3Igbm93IHdlIGNhbiBqdXN0IGRyb3AgdGhlbSBiZWNhdXNlIGFsbCB0aGVpciBpbmZvIGlzIGNvbnRhaW5lZCBpbiBvdXIgZXZlbnQgbGV2ZWwgZGF0YSBvYmplY3Q6CgpgYGB7cn0KIyBVcGRhdGUgdGliYmxlIHRvIHJlbW92ZSAncHJvYmxlbScgdmFyaWFibGVzLAp2aXNpdHMuMyA9IHNlbGVjdCh2aXNpdHMuMiwgLWMoUmVnaW9uLCBMb2NhdGlvbiwgSGFiaXRhdCwgVGVtcGVyYXR1cmUgPSBUZW1wX0MsIFNhbGluaXR5KSkgJT4lIGRpc3RpbmN0KCkKdmlzaXRzLjMgJT4lIG5yb3coKQpgYGAKClByZXR0eSBkYXJuIGNsb3NlLiBIb3BlZnVsbHkgd2UgZG9uJ3QgaGF2ZSBzYW1wbGVzIHdpdGggbXVsdGlwbGUgbWVzaCBzaXplcy4uLiAoc3BvaWxlcikgd2UgZG8uIFl1Y2suCgojIyBUaGUgbXVsdGlwbGUgZ2Vhci10eXBlIHByb2JsZW0KCkZpcnN0LCB3ZSBpc29sYXRlIHRoZSBjYXNlcyB3aGVyZSBzaW5ndWxhciB2aXNpdHMgY29udGFpbiByZXBsaWNhdGVzIG9mIGRpZmZlcmluZyBtZXNoIHNpemVzOgoKYGBge3J9CiMgTmV3IG9iamVjdCBmb3IgdmlzaXRzIHdpdGggbXVsdGlwbGUgbWVzaCBzaXplCm11bHQubWVzaCA9IHNlbGVjdCh2aXNpdHMuMywgVmlzaXRJRCwgTWVzaFNpemUpICU+JQogIHN1bW1hcmlzZShgbiBNZXNoIFNpemVzYCA9IG5fZGlzdGluY3QoTWVzaFNpemUpLCAuYnkgPSBWaXNpdElEKSAlPiUKICBmaWx0ZXIoYG4gTWVzaCBTaXplc2AgPiAxKQojIFZpZXcKbXVsdC5tZXNoCmBgYAoKRGFtbi4gTGV0J3MgZ2V0IGEgYmV0dGVyIGlkZWEgb2YgaG93IGRpZmZlcmVudCB0aGVzZSBtZXNoIHNpemVzIGFyZSwgYW5kIHdoYXQgZGF0YSBzZXRzIHRoZXkgY29tZSBmcm9tOgoKYGBge3J9CiMgSm9pbiBtdWx0aXBsZSBtZXNoIGV2ZW50cyB3aXRoIG90aGVyIGV2ZW50IGxldmVsIGluZm8sCm11bHQubWVzaC5ldmVudHMgPSBzZWxlY3QobXVsdC5tZXNoLCBWaXNpdElEKSAlPiUgbGVmdF9qb2luKGV2ZW50cywgYnkgPSAiVmlzaXRJRCIpCiMgU2tpbSB0aGUgcHJvamVjdCBhbmQgZGF0ZSBpbmZvLApzZWxlY3QobXVsdC5tZXNoLmV2ZW50cywgR2VhclNwZWNpZmljLCBQcm9qZWN0TmFtZSwgRGF0ZSkgJT4lCiAgbXV0YXRlKGFjcm9zcyhjKEdlYXJTcGVjaWZpYywgUHJvamVjdE5hbWUpLCB+IGFzX2ZhY3RvcigueCkpKSAlPiUKICBza2ltKCkKIyBTdW1tYXJ5IG9mIGdlYXIgYW5kIG1lc2ggc2l6ZSBwZXIgdmlzaXQsCnNlbGVjdChtdWx0Lm1lc2guZXZlbnRzLCBWaXNpdElELCBHZWFyU3BlY2lmaWMsIE1lc2hTaXplKSAlPiUgCiAgc3VtbWFyaXNlKG5fdmlzaXRzID0gbl9kaXN0aW5jdChWaXNpdElEKSwgLmJ5ID0gYyhHZWFyU3BlY2lmaWMsIE1lc2hTaXplKSkKYGBgCgpUaGUgZ29vZCB0aGluZyBpcyB0aGF0IEkgcmVjb2duaXplIHRoZXNlIGRhdGEuIFRoZSB2aXNpdHMgdGhhdCBjb250YWluIGRpZmZlcmVudCBtZXNoIHNpemUgZXZlbnRzIGNvbWUgZnJvbSB0aGUgc2FtZSBwcm9qZWN0OiBFUFNDb1IgRmlyZSAmIEljZSBMeW5uIENhbmFsIGdyb3VwIGluIFNvdXRoZWFzdCBBSy4gVGhleSByZXBvcnRlZCB0aGF0IGRpZmZlcmVudCBtZXNoIHNpemVzIGhhZCBhbiBlZmZlY3Qgb24gY29tbXVuaXR5IGNvbXBvc2l0aW9uIHVzaW5nIHNpZGUtYnktc2lkZSBjb21wYXJpc29uIG9mIGdlYXIgdHlwZXMgaW4gdGhlaXIgbWV0aG9kcyAoW0x1bmRzdHJvbSBldCBhbC4gMjAyMl0oaHR0cHM6Ly9kb2kub3JnLzEwLjEwMDcvczEyMjM3LTAyMi0wMTA1Ny14KSkuIFRoaXMgZXhwbGFpbnMgd2h5IHRoZSBjbHVzdGVyIGFuZCBncm91cGluZyBzdGVwcyBjb21iaW5lZCB0aGUgZXZlbnRzLgoKVGhlIGJhZCBuZXdzIGlzIHRoYXQgd2UnbGwgbmVlZCB0byByZS1kbyBwcmV2aW91cyB3cmFuZ2xpbmcgc3RlcHMgb24gd2hpY2ggbWVzaCBzaXplIGlzIGRlcG9lbmRlbnQuIFRoaXMgd2lsbCBpbnZvbHZlIHNlcGFyYXRpbmcgdGhlIGFmZmVjdGVkIFZpc2l0SUQncyBpbnRvIHNlcGFyYXRlIHNhbXBsZXMgYmFzZWQgb24gbWVzaCBzaXplLCB3aGljaCB3aWxsIHJlc3VsdCBpbiBhYm91dCAxMDAgbW9yZSB1bmlxdWUgVmlzaXRJRCdzIGFuZCBkaWZmZXJlbnQgbWVhbiBhYnVuZGFuY2UgYXNzb2NpYXRlZCBjYXRjaGVzLgoKV2UnbGwgbmVlZCB0byBzdGFydCBieSB1cGRhdGluZyBvdXIgZXZlbnRzIGxldmVsIGRhdGEgb2JqZWN0LiBTaW5jZSB0aGlzIGludm9sdmVzIG92ZXJ3cml0aW5nIGEgbWFqb3IgZGF0YSBzdGFnZSwgSSdsbCBiZSBleHBsaWNpdCBpbiB3aGF0IEknbSBkb2luZzoKCmBgYHtyfQojIENyZWF0ZSBhIHRpYmJsZSB0byBzdG9yZSB0aGUgVmlzaXRJRCdzIHRvIGJlIGZpeGVkCm11bHQubWVzaC5maXggPSBzZWxlY3QobXVsdC5tZXNoLmV2ZW50cywgVmlzaXRJRCwgRXZlbnRJRCwgR2VhclNwZWNpZmljLCBNZXNoU2l6ZSkgJT4lCiAgIyBDcmVhdGUgYSB1bmlxdWUgc3VmZml4IHRoYXQgaWRlbnRpZmllcyBtZXNoIHNpemUgdXNlZCBwZXIgZXZlbnQKICBtdXRhdGUoc3VmZml4ID0gZ3N1YigiQlNFSU5FLSIsICIiLCBHZWFyU3BlY2lmaWMpKSAlPiUKICAjIEFkZCB0aGUgc3VmZml4IHRvIFZpc2l0SUQsIGNyZWF0aW5nIG5ldyBWaXNpdElEJ3MKICB1bml0ZShjb2wgPSBWaXNpdElEX25ldywgVmlzaXRJRCwgc3VmZml4LCBzZXAgPSAiXyIpICU+JQogICMgUmV0YWluIG9ubHkgdGhlIFZpc2l0SUQncyBhbmQgdGhlaXIgRXZlbnRJRCdzCiAgc2VsZWN0KEV2ZW50SUQsIFZpc2l0SURfbmV3KQoKIyBVcGRhdGUgdGhlICdldmVudHMnIHRpYmJsZSB3aXRoIGZpeGVkIFZpc2l0SUQncywKZXZlbnRzID0gbGVmdF9qb2luKHNlbGVjdChldmVudHMsIEV2ZW50SUQsIFZpc2l0SUQpLCBtdWx0Lm1lc2guZml4LCBieSA9ICJFdmVudElEIikgJT4lCiAgIyBXaGVyZSBWaXNpdElEJ3Mgd2VyZSBub3QgZml4ZWQsIGZpbGwgaW4gd2l0aCBvbGQgVmlzaXRJRCdzCiAgbXV0YXRlKFZpc2l0SURfbmV3ID0gaWZlbHNlKGlzLm5hKFZpc2l0SURfbmV3KSwgVmlzaXRJRCwgVmlzaXRJRF9uZXcpKSAlPiUKICAjIFJlbW92ZSBvbGQgVmlzaXRJRCBjb2x1bW4KICBzZWxlY3QoLVZpc2l0SUQpICU+JQogICMgUmUtam9pbiBldmVudHMgZGF0YSAody9vIHRoZSBvbGQgVmlzaXRJRCdzKSB3aXRoIHRoZSBuZXcgVmlzaXRJRCdzLAogIGxlZnRfam9pbihzZWxlY3QoZXZlbnRzLCAtVmlzaXRJRCksIGJ5ID0gYygiRXZlbnRJRCIpKSAlPiUKICAjIFJlbmFtZSAodGhpcyBjb3VsZCBiZSBkb25lIGVhcmxpZXIgYnV0IHNlZW1zIGNsZWFyZXIgaGVyZSkKICByZW5hbWUoVmlzaXRJRCA9IFZpc2l0SURfbmV3KQpgYGAKCk5leHQsIHdlIG5lZWQgdG8gcmUtcnVuIHRoZSBjYXRjaCBhbmQgdmlzaXRzIHN0ZXBzIHRoYXQgYXJlIGJhc2VkIG9uIHRoZSBldmVudHMgZGF0YS4KCiMjIFJlLWNyZWF0aW5nIGNhdGNoIHN1bW1hcmllcyB7LnRhYnNldCAudGFic2V0LXBpbGxzfQoKV2UnbGwgbmVlZCB0byByZWRvIHRoZSBhYnVuZGFuY2UgYW5kIG9jY3VycmVuY2UgY2FsY3VsYXRpb25zIGJlY2F1c2UgdGhlIG51bWJlciBvZiB2aXNpdHMgaGFzIGNoYW5nZWQuIFJhcmUgdGF4YSB3aWxsIHN0aWxsIGJlIGRlZmluZWQgYXMgdGhvc2Ugb2NjdXJyaW5nIGluIGxlc3MgdGhhbiA1IHZpc2l0cyAoc3RpbGwgZXF1aXZhbGVudCB0byA8MC4yNSUpLiBUaGUgZnJlcXVlbmN5IGFuZCBhYnVuZGFuY2UgcGxvdHMgYXJlIG5vdCBhbGwgdGhhdCBkaWZmZXJlbnQsIGJ1dCBJIHNob3cgdXBkYXRlZCBjYXRjaCBzdW1tYXJ5IHRhYmxlcyBiZWxvdyBmb3IgZWFjaCB0YXhhIGxldmVsLiBOb3RlIHRoYXQgSSBoaWdobGlnaHQgcmFyZSB0YXhhIGluIG9yYW5nZS4KCiMjIyBGYW1pbHkgey51bm51bWJlcmVkfQoKYGBge3IgZWNobyA9IEZBTFNFfQojIyBSZS1zdW1tYXJpemUgY2F0Y2gKIyBBYnVuZGFuY2UKZmFtLmFidW4gPSBzdW1tYXJpc2UoY2F0Y2gsIEFidW5kYW5jZSA9IHN1bShDb3VudCksIC5ieSA9IEZhbV9TY2llbnRpZmljTmFtZSkKIyBGcmVxdWVuY3kgb2Ygb2NjdXJyZW5jZQpmYW0uZnJlcS5vY2N1ciA9IGxlZnRfam9pbihjYXRjaCwgc2VsZWN0KGV2ZW50cywgVmlzaXRJRCwgRXZlbnRJRCksIGJ5ID0gIkV2ZW50SUQiKSAlPiUKICBzdW1tYXJpc2UoUHJlc2VuY2UgPSBuX2Rpc3RpbmN0KEZhbV9TY2llbnRpZmljTmFtZSksIC5ieSA9IGMoVmlzaXRJRCwgRmFtX1NjaWVudGlmaWNOYW1lKSkgJT4lCiAgc3VtbWFyaXNlKE9jY3VycmVuY2UgPSBzdW0oUHJlc2VuY2UpLCAuYnkgPSBGYW1fU2NpZW50aWZpY05hbWUpICU+JQogIG11dGF0ZShQZXJjX09jY3VycmVuY2UgPSByb3VuZChPY2N1cnJlbmNlIC8gbl9kaXN0aW5jdChldmVudHMkVmlzaXRJRCkgKiAxMDAsIDIpKQojIENvbWJpbmUgY2FsY3VsYXRlZCB2YXJpYWJsZXMgYW5kIGRlZmluZSByYXJlIHRheGEKZmFtLnRibCA9IGZ1bGxfam9pbihmYW0uYWJ1biwgZmFtLmZyZXEub2NjdXIsIGJ5ID0gJ0ZhbV9TY2llbnRpZmljTmFtZScpICU+JQogIG11dGF0ZShSYXJlID0gaWZlbHNlKE9jY3VycmVuY2UgPCA1LCAiWWVzIiwgIk5vIikpCiMgU2F2ZSByYXJlIHRheGEgYXMgdmVjdG9yCnJhcmUuZmFtaWxpZXMgPSBmaWx0ZXIoZmFtLnRibCwgUmFyZSA9PSAiWWVzIikgJT4lIHB1bGwoRmFtX1NjaWVudGlmaWNOYW1lKQojIFN1bW1hcnkgdGFibGUKZmFtLnRibCAlPiUKICBzZWxlY3QoYFNjaWVudGlmaWMgTmFtZWAgPSBGYW1fU2NpZW50aWZpY05hbWUsIGBQZXJjZW50IE9jY3VycmVuY2VgID0gUGVyY19PY2N1cnJlbmNlLCBBYnVuZGFuY2UsIFJhcmUpICU+JQogIGFycmFuZ2UoYXMuY2hhcmFjdGVyKGBTY2llbnRpZmljIE5hbWVgKSkgJT4lCiAgZGF0YXRhYmxlKHJvd25hbWVzID0gRkFMU0UsCiAgICAgICAgICAgIGV4dGVuc2lvbnMgPSAnU2Nyb2xsZXInLAogICAgICAgICAgICBvcHRpb25zID0gbGlzdChkZWZlclJlbmRlciA9IFRSVUUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgIHNjcm9sbFkgPSA1MDAsCiAgICAgICAgICAgICAgICAgICAgICAgICAgIHNjcm9sbGVyID0gVFJVRSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgY29sdW1uRGVmcyA9IGxpc3QobGlzdCh0YXJnZXRzID0gJ1JhcmUnLCB2aXNpYmxlID0gRkFMU0UpKSkpICU+JSAKICBmb3JtYXRTdHlsZShjb2x1bW5zID0gIlJhcmUiLAogICAgICAgICAgICAgIHRhcmdldCA9ICJyb3ciLAogICAgICAgICAgICAgIGJhY2tncm91bmRDb2xvciA9IHN0eWxlRXF1YWwoIlllcyIsICJnb2xkZW5yb2QiKSkKYGBgCgojIyMgR2VudXMgey51bm51bWJlcmVkfQoKYGBgIHtyIGVjaG8gPSBGQUxTRX0KIyMgUmUtc3VtbWFyaXplIGNhdGNoCiMgQWJ1bmRhbmNlCmdlbi5hYnVuID0gc3VtbWFyaXNlKGNhdGNoLCBBYnVuZGFuY2UgPSBzdW0oQ291bnQpLCAuYnkgPSBHZW5fU2NpZW50aWZpY05hbWUpCiMgRnJlcXVlbmN5IG9mIG9jY3VycmVuY2UKZ2VuLmZyZXEub2NjdXIgPSBsZWZ0X2pvaW4oY2F0Y2gsIHNlbGVjdChldmVudHMsIFZpc2l0SUQsIEV2ZW50SUQpLCBieSA9ICJFdmVudElEIikgJT4lCiAgc3VtbWFyaXNlKFByZXNlbmNlID0gbl9kaXN0aW5jdChHZW5fU2NpZW50aWZpY05hbWUpLCAuYnkgPSBjKFZpc2l0SUQsIEdlbl9TY2llbnRpZmljTmFtZSkpICU+JQogIHN1bW1hcmlzZShPY2N1cnJlbmNlID0gc3VtKFByZXNlbmNlKSwgLmJ5ID0gR2VuX1NjaWVudGlmaWNOYW1lKSAlPiUKICBtdXRhdGUoUGVyY19PY2N1cnJlbmNlID0gcm91bmQoT2NjdXJyZW5jZSAvIG5fZGlzdGluY3QoZXZlbnRzJFZpc2l0SUQpICogMTAwLCAyKSkKIyBDb21iaW5lIGNhbGN1bGF0ZWQgdmFyaWFibGVzIGFuZCBkZWZpbmUgcmFyZSB0YXhhCmdlbi50YmwgPSBmdWxsX2pvaW4oZ2VuLmFidW4sIGdlbi5mcmVxLm9jY3VyLCBieSA9ICdHZW5fU2NpZW50aWZpY05hbWUnKSAlPiUKICBtdXRhdGUoUmFyZSA9IGlmZWxzZShPY2N1cnJlbmNlIDwgNSwgIlllcyIsICJObyIpKQojIFNhdmUgcmFyZSB0YXhhIGFzIHZlY3RvcgpyYXJlLmdlbnVzID0gZmlsdGVyKGdlbi50YmwsIFJhcmUgPT0gIlllcyIpICU+JSBwdWxsKEdlbl9TY2llbnRpZmljTmFtZSkKIyBTdW1tYXJ5IHRhYmxlCmdlbi50YmwgJT4lCiAgc2VsZWN0KGBTY2llbnRpZmljIE5hbWVgID0gR2VuX1NjaWVudGlmaWNOYW1lLCBgUGVyY2VudCBPY2N1cnJlbmNlYCA9IFBlcmNfT2NjdXJyZW5jZSwgQWJ1bmRhbmNlLCBSYXJlKSAlPiUKICBhcnJhbmdlKGFzLmNoYXJhY3RlcihgU2NpZW50aWZpYyBOYW1lYCkpICU+JQogIGRhdGF0YWJsZShyb3duYW1lcyA9IEZBTFNFLAogICAgICAgICAgICBleHRlbnNpb25zID0gJ1Njcm9sbGVyJywKICAgICAgICAgICAgb3B0aW9ucyA9IGxpc3QoZGVmZXJSZW5kZXIgPSBUUlVFLAogICAgICAgICAgICAgICAgICAgICAgICAgICBzY3JvbGxZID0gNTAwLAogICAgICAgICAgICAgICAgICAgICAgICAgICBzY3JvbGxlciA9IFRSVUUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbHVtbkRlZnMgPSBsaXN0KGxpc3QodGFyZ2V0cyA9ICdSYXJlJywgdmlzaWJsZSA9IEZBTFNFKSkpKSAlPiUgCiAgZm9ybWF0U3R5bGUoY29sdW1ucyA9ICJSYXJlIiwKICAgICAgICAgICAgICB0YXJnZXQgPSAicm93IiwKICAgICAgICAgICAgICBiYWNrZ3JvdW5kQ29sb3IgPSBzdHlsZUVxdWFsKCJZZXMiLCAiZ29sZGVucm9kIikpCmBgYAoKIyMjIFNwZWNpZXMgey51bm51bWJlcmVkfQoKYGBgIHtyIGVjaG8gPSBGQUxTRX0KIyMgUmUtc3VtbWFyaXplIGNhdGNoCiMgQWJ1bmRhbmNlCnNwLmFidW4gPSBzdW1tYXJpc2UoY2F0Y2gsIEFidW5kYW5jZSA9IHN1bShDb3VudCksIC5ieSA9IGMoU3BfU2NpZW50aWZpY05hbWUsIFNwX0NvbW1vbk5hbWUpKQojIEZyZXF1ZW5jeSBvZiBvY2N1cnJlbmNlCnNwLmZyZXEub2NjdXIgPSBsZWZ0X2pvaW4oY2F0Y2gsIHNlbGVjdChldmVudHMsIFZpc2l0SUQsIEV2ZW50SUQpLCBieSA9ICJFdmVudElEIikgJT4lCiAgc3VtbWFyaXNlKFByZXNlbmNlID0gbl9kaXN0aW5jdChTcF9TY2llbnRpZmljTmFtZSksIC5ieSA9IGMoVmlzaXRJRCwgU3BfU2NpZW50aWZpY05hbWUpKSAlPiUKICBzdW1tYXJpc2UoT2NjdXJyZW5jZSA9IHN1bShQcmVzZW5jZSksIC5ieSA9IFNwX1NjaWVudGlmaWNOYW1lKSAlPiUKICBtdXRhdGUoUGVyY19PY2N1cnJlbmNlID0gcm91bmQoT2NjdXJyZW5jZSAvIG5fZGlzdGluY3QoZXZlbnRzJFZpc2l0SUQpICogMTAwLCAyKSkKIyBDb21iaW5lIGNhbGN1bGF0ZWQgdmFyaWFibGVzIGFuZCBkZWZpbmUgcmFyZSB0YXhhCnNwLnRibCA9IGZ1bGxfam9pbihzcC5hYnVuLCBzcC5mcmVxLm9jY3VyLCBieSA9ICdTcF9TY2llbnRpZmljTmFtZScpICU+JQogIG11dGF0ZShSYXJlID0gaWZlbHNlKE9jY3VycmVuY2UgPCA1LCAiWWVzIiwgIk5vIikpCiMgU2F2ZSByYXJlIHRheGEgYXMgdmVjdG9yCnJhcmUuc3BlY2llcyA9IGZpbHRlcihzcC50YmwsIFJhcmUgPT0gIlllcyIpICU+JSBwdWxsKFNwX1NjaWVudGlmaWNOYW1lKQojIFN1bW1hcnkgdGFibGUKc3AudGJsICU+JQogIHNlbGVjdChgU2NpZW50aWZpYyBOYW1lYCA9IFNwX1NjaWVudGlmaWNOYW1lLCBgQ29tbW9uIE5hbWVgID0gU3BfQ29tbW9uTmFtZSwgYFBlcmNlbnQgT2NjdXJyZW5jZWAgPSBQZXJjX09jY3VycmVuY2UsIEFidW5kYW5jZSwgUmFyZSkgJT4lCiAgYXJyYW5nZShhcy5jaGFyYWN0ZXIoYFNjaWVudGlmaWMgTmFtZWApKSAlPiUKICBkYXRhdGFibGUocm93bmFtZXMgPSBGQUxTRSwKICAgICAgICAgICAgZXh0ZW5zaW9ucyA9ICdTY3JvbGxlcicsCiAgICAgICAgICAgIG9wdGlvbnMgPSBsaXN0KGRlZmVyUmVuZGVyID0gVFJVRSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgc2Nyb2xsWSA9IDUwMCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgc2Nyb2xsZXIgPSBUUlVFLAogICAgICAgICAgICAgICAgICAgICAgICAgICBjb2x1bW5EZWZzID0gbGlzdChsaXN0KHRhcmdldHMgPSAnUmFyZScsIHZpc2libGUgPSBGQUxTRSkpKSkgJT4lIAogIGZvcm1hdFN0eWxlKGNvbHVtbnMgPSAiUmFyZSIsCiAgICAgICAgICAgICAgdGFyZ2V0ID0gInJvdyIsCiAgICAgICAgICAgICAgYmFja2dyb3VuZENvbG9yID0gc3R5bGVFcXVhbCgiWWVzIiwgImdvbGRlbnJvZCIpKQpgYGAKCk5vdyB3ZSBjYW4gcmUtcnVuIHRoZSBWaXNpdElEIHdyYW5nbGluZyBjb2RlIGZyb20gZWFybGllciBpbiB0aGlzIHNlY3Rpb246CgpgYGB7cn0KIyBSZS1ydW4gdmlzaXRzLjEgY29kZSwKdmlzaXRzLjQgPSBzZWxlY3QoZXZlbnRzLCBWaXNpdElELCBFdmVudElEKSAlPiUKICBzdW1tYXJpc2UoUmVwbGljYXRlcyA9IG5fZGlzdGluY3QoRXZlbnRJRCksIC5ieSA9IFZpc2l0SUQpICU+JQogIHJpZ2h0X2pvaW4oc2VsZWN0KGV2ZW50cywgLVNpdGVJRCwgLUV2ZW50SUQpLCBieSA9ICJWaXNpdElEIikgJT4lCiAgZGlzdGluY3QoKSAlPiUKICAjIFJlLXJ1biB2aXNpdHMuMiAmIHZpc2l0cy4zIGNvZGUsCiAgc2VsZWN0KC1jKFByb2plY3ROYW1lLCBQb2ludE9mQ29udGFjdCwgR2VhclNwZWNpZmljLCBUaWRhbFN0YWdlLAogICAgICAgICAgICBSZWdpb24sIExvY2F0aW9uLCBIYWJpdGF0LCBUZW1wZXJhdHVyZSA9IFRlbXBfQywgU2FsaW5pdHkpKSAlPiUKICBkaXN0aW5jdCgpCmBgYAoKIyMgUmUtdmlzaXRpbmcgc2FtcGxlIGZyZXF1ZW5jeQoKV2Ugbm93IGhhdmUgYHIgdmlzaXRzLjQkVmlzaXRJRCAlPiUgbl9kaXN0aW5jdCgpYCB1bmlxdWUgdmlzaXRzLiBMZXQncyBkb3VibGUgY2hlY2sgdGhhdCB0aGVyZSBpcyBvbmx5IG9uZSByb3cgcGVyIFZpc2l0SUQ6CgpgYGB7cn0Kbl9kaXN0aW5jdCh2aXNpdHMuNCRWaXNpdElEKSA9PSBucm93KHZpc2l0cy40KQpgYGAKCkxldCdzIGFsc28gcmV2aWV3IG91ciBldmVudHMgcGVyIHZpc2l0IGZyZXF1ZW5jeSBwbG90IGJlY2F1c2UgdGhhdCB3aWxsIGhhdmUgY2hhbmdlZCB0b28uCgpgYGB7ciBlY2hvID0gRkFMU0V9CiMgQW5kIGV2ZW50cyBwZXIgdmlzaXQKZXZlbnRzICU+JSBzdW1tYXJpc2UoRXZlbnRzID0gbl9kaXN0aW5jdChFdmVudElEKSwgLmJ5ID0gVmlzaXRJRCkgJT4lCiAgZ2dwbG90KGRhdGEgPSAuLCBhZXMoeCA9IEV2ZW50cykpICsKICBnZW9tX2JhcihhZXMoeSA9IGFmdGVyX3N0YXQoY291bnQpKSkgKwogIGdlb21fdGV4dChzdGF0ID0gJ0NvdW50JywgYWVzKGxhYmVsID0gYWZ0ZXJfc3RhdChjb3VudCkpLCB2anVzdCA9IC0wLjUpICsKICBsYWJzKHRpdGxlID0gIkZyZXF1ZW5jeSBvZiBldmVudHMgcGVyIHNhbXBsZSBhZnRlciBzb2x2aW5nIG11bHRpcGxlIGdlYXItdHlwZSBwcm9ibGVtIikKIyBnZ3NhdmUoIm5mYV9mcmVxLWV2ZW50cy1wZXItc2FtcGxlLnBuZyIsIHBsb3QgPSBsYXN0X3Bsb3QoKSwgZGV2aWNlID0gJ3BuZycsIHBhdGggPSBmaWxlLnBhdGgoZGlyLmZpZ3MpKQpgYGAKClRoZSBkaWZmZXJlbmNlIGlzbid0IHZlcnkgbm90aWNlYWJsZS0gd2UgZW5kIHVwIHdpdGggYSBjZW50cmFsIHRlbmRlbmN5IHRoYXQgaGFzIHNsaWdodGx5IHNoaWZ0ZWQgbGVmdC4KCkxhc3QsIHdlIHNhdmUgb3VyIGZpeGVkIHZpc2l0cyB0aWJibGUgYXMgYSBuZXcgb2JqZWN0IHdpdGggYSBjbGVhbmVyIG5hbWUsIGFuZCB0aGVuIGNsZWFuIHVwIHRoZSBlbnZpcm9ubWVudCBmb3IgbmV4dCBzdGVwcy4KCmBgYHtyfQp2aXNpdHMgPSB2aXNpdHMuNApgYGAKCiMgQ2xlYW4gdXAgYW5kIHNhdmUgd29yawoKYGBge3J9CiMgUmVtb3ZlIHVud2FudGVkIG9iamVjdHMKcm0obGlzdCA9IGxzKClbIWxzKCkgJWluJSBjKCdkYXRhJywgJ2V2ZW50cycsICdjYXRjaCcsICd2aXNpdHMnLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgJ2ZhbS50YmwnLCAnZ2VuLnRibCcsICdzcC50YmwnKV0pCgojIENoZWNrIHNpemVzIG9mIHRoZSBkYXRhIG9iamVjdHMKZWFwcGx5KGFzLmVudmlyb25tZW50KC0xKSwgRlVOID0gZnVuY3Rpb24oeCkgZm9ybWF0KG9iamVjdC5zaXplKHgpLCB1bml0cyA9ICJNYiIpKQojIFRoZSBvcmlnaW5hbCAnZGF0YScgaW1wb3J0IGlzIHF1aXRlIGxhcmdlLAojIGJ1dCBhbHNvIHVubmVlZGVkIGJjIHRoZSBpbmZvIGlzIGhlbGQgaW4gb3RoZXIgb2JqZWN0cywgc28gbGV0J3MgcmVtb3ZlIGl0OgpybSgnZGF0YScpCgojIFNhdmUgd2hhdCdzIGxlZnQsCnNhdmUobGlzdCA9IGxzKCksIGZpbGUgPSBmaWxlLnBhdGgoaGVyZSgpLCAiZGF0YSIsICJORkFfd3JhbmdsZWQucmRhIikpCmBgYA==